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, None).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!(
83 router
84 .register_end_to_end_reliable_tx(&rejected, None)
85 .is_err()
86 );
87 {
88 let mut state = router.state.lock();
89 assert_eq!(state.end_to_end_reliable_tx.len(), capacity);
90 let first = state.end_to_end_reliable_tx_order[0];
91 state
92 .end_to_end_reliable_tx
93 .get_mut(&first)
94 .unwrap()
95 .retries = runtime_reliable_max_retries();
96 }
97 now.store(runtime_reliable_retransmit_ms(), Ordering::Relaxed);
98 assert!(router.process_end_to_end_reliable_timeouts().is_err());
99 assert!(router.state.lock().end_to_end_reliable_tx.is_empty());
100 }
101 #[test]
102 fn compact_reachability_survives_incomplete_detailed_topology() {
103 crate::tests::ensure_common_test_schema();
104 let router = Router::new(RouterConfig::new([]).with_sender("GB"));
105 let endpoint = DataEndpoint::try_named("GROUND_STATION")
106 .or_else(|| DataEndpoint::try_named("RADIO"))
107 .unwrap();
108 router.compact_summary_fixture(endpoint);
109 let state = router.state.lock();
110 let now = router.clock.now_ms();
111 let advertised =
112 router.advertised_discovery_endpoints_for_link_locked(&state, now, true, Some(1));
113 assert!(
114 advertised.contains(&endpoint),
115 "compact subscriber route was lost at the bridge"
116 );
117 let reflected =
118 router.advertised_discovery_endpoints_for_link_locked(&state, now, true, Some(0));
119 assert!(
120 !reflected.contains(&endpoint),
121 "split horizon must still exclude ingress"
122 );
123 assert_eq!(
124 router.advertised_discovery_timesync_sources_for_link_locked(&state, now, Some(1)),
125 vec!["clock-behind-bridge".to_string()]
126 );
127 assert!(
128 router
129 .advertised_discovery_timesync_sources_for_link_locked(&state, now, Some(0))
130 .is_empty()
131 );
132 assert!(
133 !router
134 .advertised_discovery_endpoints_for_link_locked(
135 &state,
136 now + DISCOVERY_ROUTE_TTL_MS + 1,
137 true,
138 Some(1)
139 )
140 .contains(&endpoint)
141 );
142 assert!(
143 router
144 .advertised_discovery_timesync_sources_for_link_locked(
145 &state,
146 now + DISCOVERY_ROUTE_TTL_MS + 1,
147 Some(1)
148 )
149 .is_empty()
150 );
151 }
152}
153
154use crate::config::RuntimeMemoryConfig;
155use crate::diagnostics::{
156 AdaptiveLinkStats, DiscoveryRuntimeStats, QueueRuntimeStats, ReliableRuntimeStats,
157 RouteModeStats, RouteOverrideStats, RoutePriorityStats, RouteWeightStats, RuntimeSideStats,
158 RuntimeStatsSnapshot, RuntimeTypeStats, TypedRouteOverrideStats,
159};
160#[cfg(feature = "discovery")]
161use crate::discovery::{
162 self, ClientStatsSnapshot, DISCOVERY_INCREMENTAL_RETRY_COUNT, DISCOVERY_ROUTE_TTL_MS,
163 DISCOVERY_SLOW_LINK_PING_INTERVAL_MS, DiscoveryCadenceState,
164 TIMESYNC_SLOW_LINK_MIN_INTERVAL_MS, TopologyAnnouncerRoute, TopologyBoardNode,
165 TopologySideRoute, TopologySnapshot,
166};
167use crate::packet::{hash_bytes_u64, sender_address_u32};
168use crate::queue::{BoundedDeque, ByteCost};
169#[cfg(all(not(feature = "std"), target_os = "none"))]
170use crate::seds_error_msg;
171#[cfg(feature = "timesync")]
172use crate::timesync::{
173 INTERNAL_TIMESYNC_SOURCE_ID, LOCAL_TIMESYNC_DATE_SOURCE_ID, LOCAL_TIMESYNC_FULL_SOURCE_ID,
174 LOCAL_TIMESYNC_SUBSEC_SOURCE_ID, LOCAL_TIMESYNC_TOD_SOURCE_ID, NetworkClock,
175 NetworkTimeReading, PartialNetworkTime, SlewedNetworkClock, TimeSyncConfig, TimeSyncLeader,
176 TimeSyncTracker, advance_network_time, compute_network_time_sample, decode_timesync_announce,
177 decode_timesync_request, decode_timesync_response,
178};
179use crate::{
180 E2eEncryptionPolicy, MessageElement, RouteSelectionMode, TelemetryError, TelemetryResult,
181 config::{
182 DataEndpoint, DataType, runtime_device_identifier, runtime_max_handler_retries,
183 runtime_reliable_max_end_to_end_pending, runtime_reliable_max_pending,
184 runtime_reliable_max_retries, runtime_reliable_max_return_routes,
185 runtime_reliable_retransmit_ms,
186 },
187 get_needed_message_size, impl_letype_num, is_reliable_type,
188 lock::{ReentryGate, RouterMutex},
189 message_e2e_encryption_policy, message_meta, message_priority,
190 packet::Packet,
191 reliable_mode, wire_format,
192};
193use alloc::string::{String, ToString};
194use alloc::{
195 borrow::ToOwned,
196 boxed::Box,
197 collections::{BTreeMap, BTreeSet, VecDeque},
198 format,
199 sync::Arc,
200 vec,
201 vec::Vec,
202};
203use core::cell::UnsafeCell;
204use core::fmt;
205use core::fmt::{Debug, Formatter};
206use core::mem::size_of;
207use core::ops::{Deref, DerefMut};
208use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
209#[cfg(feature = "std")]
210use std::time::Instant;
211
212pub type RouterSideId = usize;
214
215const CONTROL_SLOW_LINK_CAPACITY_BPS: u64 = 512;
216pub const IPV4_LIKE_COMPACT_HEADER_TARGET_BYTES: usize = 20;
217pub const IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES: usize = 40;
218pub const DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT: usize = 64;
219static ROUTER_INSTANCE_SEQ: AtomicU32 = AtomicU32::new(1);
220
221#[derive(Clone, Copy, Debug, PartialEq, Eq)]
222pub enum SideTransportProfile {
223 Canonical,
224 Template,
225 Ipv6Like,
226 Ipv4Like,
227}
228
229impl SideTransportProfile {
230 #[inline]
231 pub const fn as_str(self) -> &'static str {
232 match self {
233 Self::Canonical => "canonical",
234 Self::Template => "template",
235 Self::Ipv6Like => "ipv6_like",
236 Self::Ipv4Like => "ipv4_like",
237 }
238 }
239
240 #[cfg(feature = "discovery")]
241 #[inline]
242 pub const fn discovery_code(self) -> u8 {
243 match self {
244 Self::Canonical => discovery::LINK_PROFILE_CANONICAL,
245 Self::Template => discovery::LINK_PROFILE_TEMPLATE,
246 Self::Ipv6Like => discovery::LINK_PROFILE_IPV6_LIKE,
247 Self::Ipv4Like => discovery::LINK_PROFILE_IPV4_LIKE,
248 }
249 }
250}
251
252#[cfg(test)]
253mod chunk_assembly_memory_tests {
254 use super::*;
255 #[test]
256 fn full_chunk_budget_reclaims_other_partial_transfers_before_active_schema() {
257 let mut state = SideTransportState::default();
258 for id in 1..=4 {
259 let mut assembly = SideChunkAssembly {
260 total: 28,
261 last_seen_ms: id as u64,
262 ..Default::default()
263 };
264 for index in 0..23 {
265 assembly.received.insert(
266 index,
267 crate::shared_bytes::convert(Arc::<[u8]>::from([id as u8; 112])),
268 );
269 }
270 state.rx_chunks.insert(id, assembly);
271 }
272 state.rx_chunks.get_mut(&1).unwrap().received.insert(
273 23,
274 crate::shared_bytes::convert(Arc::<[u8]>::from([1u8; 56])),
275 );
276 SideChunkAssembly::make_room(&mut state.rx_chunks, 4, 23, 28, 112, 16384).unwrap();
278 assert!(!state.rx_chunks.contains_key(&1));
279 assert_eq!(state.rx_chunks[&4].received.len(), 23);
280 for index in 23..28 {
281 SideChunkAssembly::make_room(&mut state.rx_chunks, 4, index, 28, 112, 16384).unwrap();
282 state.rx_chunks.get_mut(&4).unwrap().received.insert(
283 index,
284 crate::shared_bytes::convert(Arc::<[u8]>::from([4u8; 112])),
285 );
286 }
287 assert_eq!(
288 state.rx_chunks.remove(&4).unwrap().assemble().unwrap(),
289 vec![4u8; 28 * 112]
290 );
291 assert!(
292 state
293 .rx_chunks
294 .values()
295 .flat_map(|a| a.received.values())
296 .map(|p| p.len() + 64)
297 .sum::<usize>()
298 <= 16384
299 );
300 }
301
302 #[test]
303 fn duplicate_chunk_keeps_partial_transfers_but_oversize_transfer_is_rejected() {
304 let mut state = SideTransportState::default();
305 let mut assembly = SideChunkAssembly {
306 total: 2,
307 ..Default::default()
308 };
309 assembly.received.insert(
310 0,
311 crate::shared_bytes::convert(Arc::<[u8]>::from([1u8; 112])),
312 );
313 state.rx_chunks.insert(1, assembly.clone());
314 state.rx_chunks.insert(2, assembly);
315 SideChunkAssembly::make_room(&mut state.rx_chunks, 1, 0, 2, 112, 352).unwrap();
316 assert_eq!(state.rx_chunks.len(), 2);
317 assert!(SideChunkAssembly::make_room(&mut state.rx_chunks, 1, 1, 2, 400, 352).is_err());
318 assert!(!state.rx_chunks.contains_key(&1));
319 assert!(state.rx_chunks.contains_key(&2));
320 }
321
322 #[test]
323 fn schema_sized_reassembly_allocates_exactly_and_preserves_order() {
324 let payload: Vec<u8> = (0..3652).map(|i| (i % 251) as u8).collect();
325 let mut assembly = SideChunkAssembly::default();
326 for (index, chunk) in payload.chunks(56).enumerate().rev() {
327 assembly.received.insert(
328 index as u16,
329 crate::shared_bytes::convert(Arc::<[u8]>::from(chunk)),
330 );
331 }
332 assembly.total = assembly.received.len() as u16;
333 let out = assembly.assemble().unwrap();
334 assert_eq!(out, payload);
335 assert_eq!(
336 out.capacity(),
337 payload.len(),
338 "do not double discovery reassembly capacity"
339 );
340 }
341 #[test]
342 fn incomplete_or_noncontiguous_chunks_are_rejected() {
343 let mut assembly = SideChunkAssembly {
344 total: 2,
345 ..Default::default()
346 };
347 assembly
348 .received
349 .insert(0, crate::shared_bytes::convert(Arc::<[u8]>::from([1u8])));
350 assert!(assembly.clone().assemble().is_err());
351 assembly
352 .received
353 .insert(2, crate::shared_bytes::convert(Arc::<[u8]>::from([2u8])));
354 assert!(assembly.assemble().is_err());
355 }
356}
357
358#[derive(Clone, Debug, Default)]
359struct SideTransportState {
360 tx_chunk_progress: Option<Box<crate::side_transport::SideTxProgress>>,
361 tx_template_ids: BTreeMap<u64, u32>,
362 tx_templates: BTreeMap<u64, SideHeaderTemplate>,
363 tx_last_timestamps: BTreeMap<u32, u64>,
364 tx_compact_uses: BTreeMap<u32, u8>,
365 rx_templates: BTreeMap<u64, u32>,
366 rx_templates_by_id: BTreeMap<u32, SideHeaderTemplate>,
367 rx_last_timestamps: BTreeMap<u32, u64>,
368 rx_chunks: BTreeMap<u32, SideChunkAssembly>,
369}
370
371impl SideTransportState {
372 fn refresh_transmit_header_templates(&mut self) {
373 for uses in self.tx_compact_uses.values_mut() {
377 *uses = u8::MAX;
378 }
379 }
382
383 fn tx_template_count(&self) -> usize {
384 self.tx_template_ids.len()
385 }
386
387 fn rx_template_count(&self) -> usize {
388 self.rx_templates_by_id.len()
389 }
390
391 fn trim_tx_templates_to(&mut self, limit: usize) -> usize {
392 let mut removed = 0usize;
393 while self.tx_template_ids.len() > limit {
394 let Some(old_hash) = self.tx_template_ids.keys().next().copied() else {
395 break;
396 };
397 if let Some(old_id) = self.tx_template_ids.remove(&old_hash) {
398 self.tx_last_timestamps.remove(&old_id);
399 self.tx_compact_uses.remove(&old_id);
400 }
401 self.tx_templates.remove(&old_hash);
402 removed += 1;
403 }
404 removed
405 }
406
407 fn insert_tx_template(
408 &mut self,
409 template: SideHeaderTemplate,
410 template_id: u32,
411 max_templates: usize,
412 ) -> bool {
413 if max_templates == 0 {
414 return false;
415 }
416 let evicted = !self.tx_template_ids.contains_key(&template.hash)
417 && self.trim_tx_templates_to(max_templates.saturating_sub(1)) > 0;
418 self.tx_template_ids.insert(template.hash, template_id);
419 self.tx_templates.insert(template.hash, template);
420 self.tx_compact_uses.insert(template_id, 0);
421 evicted
422 }
423
424 fn insert_rx_template(
425 &mut self,
426 template_id: u32,
427 template: SideHeaderTemplate,
428 max_templates: usize,
429 ) -> bool {
430 if max_templates == 0 {
431 return false;
432 }
433 if let Some(old_id) = self.rx_templates.get(&template.hash).copied()
436 && old_id != template_id
437 {
438 self.rx_templates_by_id.remove(&old_id);
439 self.rx_last_timestamps.remove(&old_id);
440 self.rx_templates.remove(&template.hash);
441 }
442 if let Some(previous) = self.rx_templates_by_id.get(&template_id)
443 && previous.hash != template.hash
444 {
445 self.rx_templates.remove(&previous.hash);
446 self.rx_last_timestamps.remove(&template_id);
447 }
448 let mut evicted = false;
449 if self.rx_templates_by_id.len() >= max_templates
450 && !self.rx_templates_by_id.contains_key(&template_id)
451 && let Some((&old_hash, &old_id)) = self.rx_templates.first_key_value()
454 {
455 self.rx_templates.remove(&old_hash);
456 self.rx_templates_by_id.remove(&old_id);
457 self.rx_last_timestamps.remove(&old_id);
458 evicted = true;
459 }
460 self.rx_templates.insert(template.hash, template_id);
461 self.rx_templates_by_id.insert(template_id, template);
462 evicted
463 }
464}
465
466#[derive(Clone, Debug, PartialEq, Eq)]
467pub struct CompactTimestampOmissionPolicy {
468 all: bool,
469 types: Option<Arc<[DataType]>>,
470}
471impl CompactTimestampOmissionPolicy {
472 pub const fn none() -> Self {
473 Self {
474 all: false,
475 types: None,
476 }
477 }
478 pub const fn all() -> Self {
479 Self {
480 all: true,
481 types: None,
482 }
483 }
484 pub fn with_data_type(mut self, ty: DataType) -> Self {
485 self.insert(ty);
486 self
487 }
488 pub fn insert(&mut self, ty: DataType) {
489 if self.contains(ty) {
490 return;
491 }
492 let current = self.types.as_deref().unwrap_or(&[]);
493 let mut types = Vec::with_capacity(current.len() + 1);
494 types.extend_from_slice(current);
495 let index = types.binary_search(&ty).unwrap_err();
496 types.insert(index, ty);
497 self.types = Some(Arc::from(types));
498 }
499 pub fn contains(&self, ty: DataType) -> bool {
500 self.all
501 || self
502 .types
503 .as_deref()
504 .is_some_and(|types| types.binary_search(&ty).is_ok())
505 }
506 pub fn is_empty(&self) -> bool {
507 !self.all && self.types.is_none()
508 }
509}
510
511impl Default for CompactTimestampOmissionPolicy {
512 #[inline]
513 fn default() -> Self {
514 Self::none()
515 }
516}
517
518#[derive(Clone, Debug, PartialEq, Eq)]
519pub enum RouterItem {
520 Packet(Packet),
521 Packed(crate::SharedBytes),
522}
523
524#[cfg(feature = "compact-packet-store")]
525impl RouterItem {
526 fn park_for_queue(&mut self) -> TelemetryResult<()> {
527 match self {
528 Self::Packet(packet) => packet.park_for_queue()?,
529 Self::Packed(bytes) => bytes.park_for_queue()?,
530 }
531 Ok(())
532 }
533}
534
535#[derive(Clone, Debug, PartialEq, Eq)]
536struct RouterRxItem {
537 src: Option<RouterSideId>,
538 data: RouterItem,
539 priority: u8,
540}
541
542#[derive(Clone, Debug, PartialEq, Eq)]
543enum RouterTxItem {
544 Broadcast(RouterItem),
545 EndToEndReplay {
546 packet_id: u64,
547 },
548 ToSide {
549 src: Option<RouterSideId>,
550 dst: RouterSideId,
551 data: RouterItem,
552 },
553 ReliableReplay {
554 dst: RouterSideId,
555 bytes: crate::SharedBytes,
556 },
557}
558
559impl ByteCost for RouterRxItem {
560 #[cfg(feature = "compact-packet-store")]
561 fn park_for_queue(&mut self) -> TelemetryResult<()> {
562 self.data.park_for_queue()
563 }
564 #[inline]
565 fn byte_cost(&self) -> usize {
566 match &self.data {
567 RouterItem::Packet(pkt) => pkt.byte_cost(),
568 RouterItem::Packed(bytes) => size_of::<crate::SharedBytes>() + bytes.len(),
569 }
570 }
571}
572
573impl ByteCost for RouterTxItem {
574 #[cfg(feature = "compact-packet-store")]
575 fn park_for_queue(&mut self) -> TelemetryResult<()> {
576 match self {
577 Self::Broadcast(item) | Self::ToSide { data: item, .. } => item.park_for_queue(),
578 Self::ReliableReplay { bytes, .. } => bytes.park_for_queue(),
579 _ => Ok(()),
580 }
581 }
582 #[inline]
583 fn byte_cost(&self) -> usize {
584 match self {
585 RouterTxItem::Broadcast(data) => match data {
586 RouterItem::Packet(pkt) => pkt.byte_cost(),
587 RouterItem::Packed(bytes) => size_of::<crate::SharedBytes>() + bytes.len(),
588 },
589 RouterTxItem::EndToEndReplay { .. } => size_of::<u64>(),
590 RouterTxItem::ToSide { data, .. } => match data {
591 RouterItem::Packet(pkt) => pkt.byte_cost(),
592 RouterItem::Packed(bytes) => size_of::<crate::SharedBytes>() + bytes.len(),
593 },
594 RouterTxItem::ReliableReplay { bytes, .. } => {
595 size_of::<crate::SharedBytes>() + bytes.len()
596 }
597 }
598 }
599}
600
601#[derive(Clone, Debug, PartialEq, Eq)]
605struct TxQueued {
606 item: RouterTxItem,
607 ignore_local: bool,
608 priority: u8,
609 retry_after_ms: u64,
610}
611
612const MAX_PRIORITY_BURST: u8 = 8;
617
618impl ByteCost for TxQueued {
620 #[cfg(feature = "compact-packet-store")]
621 fn park_for_queue(&mut self) -> TelemetryResult<()> {
622 self.item.park_for_queue()
623 }
624 #[inline]
626 fn byte_cost(&self) -> usize {
627 self.item.byte_cost() + size_of::<bool>() + size_of::<u8>() + size_of::<u64>()
628 }
629}
630
631impl ByteCost for u64 {
633 #[inline]
635 fn byte_cost(&self) -> usize {
636 size_of::<u64>()
637 }
638}
639
640#[derive(Debug, Clone)]
643struct ReliableTxState {
644 next_seq: u32,
645 sent_order: VecDeque<u32>,
646 sent: BTreeMap<u32, ReliableSent>,
647}
648
649#[derive(Debug, Clone)]
650struct ReliableSent {
651 bytes: crate::SharedBytes,
652 last_send_ms: u64,
653 retries: u32,
654 queued: bool,
655 partial_acked: bool,
656}
657
658#[derive(Debug, Clone)]
659struct EndToEndReliableSent {
660 ordered_type: Option<u32>,
661 waiting_for_predecessor: bool,
662 data: RouterItem,
663 pending_destinations: BTreeMap<u64, RouterSideId>,
664 tracked_destinations: bool,
665 last_send_ms: u64,
666 retries: u32,
667 queued: bool,
668}
669
670#[derive(Debug, Clone)]
671struct ReliableRxState {
672 expected_seq: u32,
673 buffered: BTreeMap<u32, crate::SharedBytes>,
674}
675
676#[derive(Debug, Clone)]
677struct ReliableReturnRouteState {
678 side: RouterSideId,
679 last_replay_ms: u64,
680 upstream_contract: Option<(u64, u64)>,
684}
685
686#[cfg(feature = "discovery")]
687#[derive(Debug, Clone, Default, PartialEq, Eq)]
688struct DiscoverySenderState {
689 advertised_address: Option<u32>,
692 reachable: Vec<DataEndpoint>,
693 advertised_reachable: Vec<DataEndpoint>,
694 reachable_network_variables: Vec<DataType>,
695 reachable_timesync_sources: Vec<String>,
696 advertised_reachable_timesync_sources: Vec<String>,
697 link_capabilities: Option<discovery::LinkCapabilities>,
698 topology_boards: Vec<TopologyBoardNode>,
699 has_full_topology: bool,
700 has_schema: bool,
701 last_seen_ms: u64,
702}
703
704#[cfg(feature = "discovery")]
705#[derive(Debug, Clone, Default, PartialEq, Eq)]
706struct DiscoverySideState {
707 reachable: Vec<DataEndpoint>,
708 reachable_network_variables: Vec<DataType>,
709 requested_network_variables: Vec<(DataType, u64)>,
710 reachable_timesync_sources: Vec<String>,
711 last_seen_ms: u64,
712 announcers: BTreeMap<String, DiscoverySenderState>,
713}
714
715#[cfg(feature = "discovery")]
716#[derive(Debug, Clone, Default)]
717struct DiscoverySideThrottleState {
718 next_topology_request_ms: u64,
719 next_schema_request_ms: u64,
720 next_ping_ms: u64,
721 last_liveness_tx_ms: u64,
723 pending_incremental: bool,
724 incremental_retries_remaining: u8,
725 has_sent_full: bool,
726 last_topology: Vec<TopologyBoardNode>,
727}
728
729#[cfg(feature = "discovery")]
730impl DiscoverySideThrottleState {
731 fn liveness_due(&self, now_ms: u64) -> bool {
732 now_ms >= self.next_ping_ms
733 && now_ms.saturating_sub(self.last_liveness_tx_ms)
734 >= DISCOVERY_SLOW_LINK_PING_INTERVAL_MS
735 }
736}
737
738#[cfg(all(feature = "discovery", feature = "timesync"))]
739#[derive(Debug, Clone, Default)]
740struct TimeSyncSideThrottleState {
741 next_allowed_ms: u64,
742}
743
744#[cfg(feature = "discovery")]
745#[derive(Debug, Clone, Copy, PartialEq, Eq)]
746enum DiscoveryAdvertiseLevel {
747 MinimalPing,
748 Incremental,
749 Full,
750}
751
752#[derive(Debug, Clone, Default)]
753struct AdaptiveRouteStats {
754 estimated_bandwidth_bps: u64,
755 peak_bandwidth_bps: u64,
756 last_observed_ms: u64,
757 last_slow_observed_ms: u64,
758 sample_count: u64,
759 window_started_ms: u64,
760 window_bytes: u64,
761 peak_usage_bps: u64,
762}
763
764impl AdaptiveRouteStats {
765 #[inline]
766 fn observe(&mut self, bytes: usize, sample_bps: u64, now_ms: u64) {
767 self.estimated_bandwidth_bps = if self.estimated_bandwidth_bps == 0 {
768 sample_bps
769 } else if sample_bps >= self.estimated_bandwidth_bps {
770 self.estimated_bandwidth_bps
771 .saturating_mul(3)
772 .saturating_add(sample_bps.saturating_mul(5))
773 / 8
774 } else {
775 self.estimated_bandwidth_bps
776 .saturating_mul(7)
777 .saturating_add(sample_bps)
778 / 8
779 };
780 self.peak_bandwidth_bps = self.peak_bandwidth_bps.max(sample_bps);
781 self.last_observed_ms = now_ms;
782 if sample_bps > 0 && sample_bps <= CONTROL_SLOW_LINK_CAPACITY_BPS {
783 self.last_slow_observed_ms = now_ms;
784 }
785 self.sample_count = self.sample_count.saturating_add(1);
786 if self.window_started_ms == 0 || now_ms.saturating_sub(self.window_started_ms) > 1_000 {
787 self.window_started_ms = now_ms;
788 self.window_bytes = 0;
789 }
790 self.window_bytes = self.window_bytes.saturating_add(bytes as u64);
791 self.peak_usage_bps = self.peak_usage_bps.max(self.current_usage_bps(now_ms));
792 }
793
794 #[inline]
795 fn current_usage_bps(&self, now_ms: u64) -> u64 {
796 if self.window_started_ms == 0 {
797 return 0;
798 }
799 let elapsed_ms = now_ms.saturating_sub(self.window_started_ms).max(1);
800 (u128::from(self.window_bytes).saturating_mul(1000) / u128::from(elapsed_ms))
801 .min(u128::from(u64::MAX)) as u64
802 }
803
804 #[inline]
805 fn available_headroom_bps(&self, now_ms: u64) -> u64 {
806 let capacity = self
807 .estimated_bandwidth_bps
808 .max(self.peak_bandwidth_bps)
809 .max(1);
810 capacity.saturating_sub(self.current_usage_bps(now_ms))
811 }
812
813 #[inline]
814 fn weight(&self, now_ms: u64) -> u64 {
815 self.available_headroom_bps(now_ms).max(1)
816 }
817
818 #[inline]
819 fn snapshot(&self, now_ms: u64, auto_balancing_enabled: bool) -> AdaptiveLinkStats {
820 let current_usage_bps = self.current_usage_bps(now_ms);
821 let estimated_capacity_bps = self.estimated_bandwidth_bps.max(1);
822 let peak_capacity_bps = self.peak_bandwidth_bps.max(estimated_capacity_bps);
823 let available_headroom_bps = peak_capacity_bps.saturating_sub(current_usage_bps);
824 AdaptiveLinkStats {
825 auto_balancing_enabled,
826 estimated_capacity_bps,
827 peak_capacity_bps,
828 current_usage_bps,
829 peak_usage_bps: self.peak_usage_bps.max(current_usage_bps),
830 available_headroom_bps,
831 effective_weight: available_headroom_bps.max(1),
832 last_observed_ms: self.last_observed_ms,
833 sample_count: self.sample_count,
834 }
835 }
836}
837
838#[derive(Debug, Clone, Default)]
839struct TypeRuntimeStatsInner {
840 tx_packets: u64,
841 tx_bytes: u64,
842 rx_packets: u64,
843 rx_bytes: u64,
844 relayed_tx_packets: u64,
845 relayed_tx_bytes: u64,
846 relayed_rx_packets: u64,
847 relayed_rx_bytes: u64,
848 tx_retries: u64,
849 handler_failures: u64,
850}
851
852#[derive(Debug, Clone, Default)]
853struct SideRuntimeStatsInner {
854 tx_packets: u64,
855 tx_bytes: u64,
856 rx_packets: u64,
857 rx_bytes: u64,
858 relayed_tx_packets: u64,
859 relayed_tx_bytes: u64,
860 relayed_rx_packets: u64,
861 relayed_rx_bytes: u64,
862 local_delivery_packets: u64,
863 tx_retries: u64,
864 tx_handler_failures: u64,
865 local_handler_failures: u64,
866 total_handler_retries: u64,
867 side_transport_full_frames: u64,
868 side_transport_compact_frames: u64,
869 side_transport_compact_delta_frames: u64,
870 side_transport_compact_omitted_timestamp_frames: u64,
871 side_transport_chunk_frames: u64,
872 side_transport_raw_bytes: u64,
873 side_transport_wire_bytes: u64,
874 side_transport_bytes_saved: u64,
875 side_transport_min_compact_overhead_bytes: Option<usize>,
876 side_transport_max_compact_overhead_bytes: Option<usize>,
877 side_transport_compact_target_misses: u64,
878 side_transport_template_evictions: u64,
879 data_types: BTreeMap<u32, TypeRuntimeStatsInner>,
880}
881
882impl SideRuntimeStatsInner {
883 fn type_stats_mut(&mut self, ty: DataType) -> &mut TypeRuntimeStatsInner {
884 self.data_types.entry(ty.as_u32()).or_default()
885 }
886
887 fn note_tx(&mut self, ty: DataType, bytes: usize, relayed: bool, retries: usize) {
888 self.tx_packets = self.tx_packets.saturating_add(1);
889 self.tx_bytes = self.tx_bytes.saturating_add(bytes as u64);
890 self.tx_retries = self.tx_retries.saturating_add(retries as u64);
891 self.total_handler_retries = self.total_handler_retries.saturating_add(retries as u64);
892 if relayed {
893 self.relayed_tx_packets = self.relayed_tx_packets.saturating_add(1);
894 self.relayed_tx_bytes = self.relayed_tx_bytes.saturating_add(bytes as u64);
895 }
896 let stats = self.type_stats_mut(ty);
897 stats.tx_packets = stats.tx_packets.saturating_add(1);
898 stats.tx_bytes = stats.tx_bytes.saturating_add(bytes as u64);
899 stats.tx_retries = stats.tx_retries.saturating_add(retries as u64);
900 if relayed {
901 stats.relayed_tx_packets = stats.relayed_tx_packets.saturating_add(1);
902 stats.relayed_tx_bytes = stats.relayed_tx_bytes.saturating_add(bytes as u64);
903 }
904 }
905
906 fn note_rx(&mut self, ty: DataType, bytes: usize, relayed: bool) {
907 self.rx_packets = self.rx_packets.saturating_add(1);
908 self.rx_bytes = self.rx_bytes.saturating_add(bytes as u64);
909 if relayed {
910 self.relayed_rx_packets = self.relayed_rx_packets.saturating_add(1);
911 self.relayed_rx_bytes = self.relayed_rx_bytes.saturating_add(bytes as u64);
912 }
913 let stats = self.type_stats_mut(ty);
914 stats.rx_packets = stats.rx_packets.saturating_add(1);
915 stats.rx_bytes = stats.rx_bytes.saturating_add(bytes as u64);
916 if relayed {
917 stats.relayed_rx_packets = stats.relayed_rx_packets.saturating_add(1);
918 stats.relayed_rx_bytes = stats.relayed_rx_bytes.saturating_add(bytes as u64);
919 }
920 }
921
922 fn note_local_delivery(&mut self, ty: DataType) {
923 self.local_delivery_packets = self.local_delivery_packets.saturating_add(1);
924 let stats = self.type_stats_mut(ty);
925 stats.rx_packets = stats.rx_packets.saturating_add(1);
926 }
927
928 fn note_local_handler_failure(&mut self, ty: DataType, retries: usize) {
929 self.local_handler_failures = self.local_handler_failures.saturating_add(1);
930 self.total_handler_retries = self.total_handler_retries.saturating_add(retries as u64);
931 let stats = self.type_stats_mut(ty);
932 stats.handler_failures = stats.handler_failures.saturating_add(1);
933 }
934
935 fn note_tx_failure(&mut self, ty: DataType, retries: usize) {
936 self.tx_handler_failures = self.tx_handler_failures.saturating_add(1);
937 self.total_handler_retries = self.total_handler_retries.saturating_add(retries as u64);
938 self.tx_retries = self.tx_retries.saturating_add(retries as u64);
939 let stats = self.type_stats_mut(ty);
940 stats.handler_failures = stats.handler_failures.saturating_add(1);
941 stats.tx_retries = stats.tx_retries.saturating_add(retries as u64);
942 }
943
944 fn note_side_transport_full(&mut self, raw_bytes: usize, wire_bytes: usize) {
945 self.side_transport_full_frames = self.side_transport_full_frames.saturating_add(1);
946 self.note_side_transport_bytes(raw_bytes, wire_bytes);
947 }
948
949 fn note_side_transport_compact(
950 &mut self,
951 raw_bytes: usize,
952 wire_bytes: usize,
953 compact_overhead_bytes: usize,
954 used_timestamp_delta: bool,
955 omitted_timestamp: bool,
956 ) {
957 self.side_transport_compact_frames = self.side_transport_compact_frames.saturating_add(1);
958 if used_timestamp_delta {
959 self.side_transport_compact_delta_frames =
960 self.side_transport_compact_delta_frames.saturating_add(1);
961 }
962 if omitted_timestamp {
963 self.side_transport_compact_omitted_timestamp_frames = self
964 .side_transport_compact_omitted_timestamp_frames
965 .saturating_add(1);
966 }
967 self.note_side_transport_bytes(raw_bytes, wire_bytes);
968 self.side_transport_min_compact_overhead_bytes = Some(
969 self.side_transport_min_compact_overhead_bytes
970 .map_or(compact_overhead_bytes, |v| v.min(compact_overhead_bytes)),
971 );
972 self.side_transport_max_compact_overhead_bytes = Some(
973 self.side_transport_max_compact_overhead_bytes
974 .map_or(compact_overhead_bytes, |v| v.max(compact_overhead_bytes)),
975 );
976 }
977
978 fn note_side_transport_chunks(&mut self, chunks: usize) {
979 self.side_transport_chunk_frames = self
980 .side_transport_chunk_frames
981 .saturating_add(chunks as u64);
982 }
983
984 fn note_side_transport_bytes(&mut self, raw_bytes: usize, wire_bytes: usize) {
985 self.side_transport_raw_bytes = self
986 .side_transport_raw_bytes
987 .saturating_add(raw_bytes as u64);
988 self.side_transport_wire_bytes = self
989 .side_transport_wire_bytes
990 .saturating_add(wire_bytes as u64);
991 if raw_bytes > wire_bytes {
992 self.side_transport_bytes_saved = self
993 .side_transport_bytes_saved
994 .saturating_add((raw_bytes - wire_bytes) as u64);
995 }
996 }
997
998 fn note_side_transport_compact_target_miss(&mut self) {
999 self.side_transport_compact_target_misses =
1000 self.side_transport_compact_target_misses.saturating_add(1);
1001 }
1002
1003 fn note_side_transport_template_eviction(&mut self) {
1004 self.side_transport_template_evictions =
1005 self.side_transport_template_evictions.saturating_add(1);
1006 }
1007}
1008
1009#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1010enum RouteSelectionOrigin {
1011 Flood,
1012 Discovered,
1013}
1014
1015type PacketHandlerFn = dyn Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static;
1018
1019type PackedHandlerFn = dyn Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static;
1021type PrioritizedPackedHandlerFn = dyn Fn(&[u8], u8) -> TelemetryResult<()> + Send + Sync + 'static;
1022
1023#[derive(Clone)]
1029pub enum EndpointHandlerFn {
1030 Packet(Arc<PacketHandlerFn>),
1031 Packed(Arc<PackedHandlerFn>),
1032}
1033
1034pub struct EndpointHandler {
1036 endpoint: DataEndpoint,
1037 handler: EndpointHandlerFn,
1038}
1039
1040impl Debug for EndpointHandlerFn {
1042 #[inline]
1043 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1044 match self {
1045 EndpointHandlerFn::Packet(_) => f.write_str("EndpointHandlerFn::Packet(<handler>)"),
1046 EndpointHandlerFn::Packed(_) => f.write_str("EndpointHandlerFn::Packed(<handler>)"),
1047 }
1048 }
1049}
1050
1051#[derive(Clone)]
1053pub enum RouterTxHandlerFn {
1054 Packed(Arc<PackedHandlerFn>),
1055 PackedWithPriority(Arc<PrioritizedPackedHandlerFn>),
1056 Packet(Arc<PacketHandlerFn>),
1057}
1058
1059impl Debug for RouterTxHandlerFn {
1060 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1061 match self {
1062 RouterTxHandlerFn::Packed(_) => f.debug_tuple("Packed").field(&"<handler>").finish(),
1063 RouterTxHandlerFn::PackedWithPriority(_) => f
1064 .debug_tuple("PackedWithPriority")
1065 .field(&"<handler>")
1066 .finish(),
1067 RouterTxHandlerFn::Packet(_) => f.debug_tuple("Packet").field(&"<handler>").finish(),
1068 }
1069 }
1070}
1071
1072#[derive(Clone, Debug)]
1073pub struct RouterSideOptions {
1074 pub reliable_enabled: bool,
1085 pub link_local_enabled: bool,
1087 pub header_template_enabled: bool,
1094 pub max_frame_bytes: usize,
1100 pub compact_header_target_bytes: usize,
1107 pub max_side_transport_templates: usize,
1113 pub omit_unchanged_compact_timestamps: bool,
1119 pub compact_timestamp_omission_types: CompactTimestampOmissionPolicy,
1124 pub side_transport_profile: SideTransportProfile,
1126 pub ingress_enabled: bool,
1128 pub egress_enabled: bool,
1130}
1131
1132impl Default for RouterSideOptions {
1133 fn default() -> Self {
1134 Self {
1135 reliable_enabled: false,
1136 link_local_enabled: false,
1137 header_template_enabled: false,
1138 max_frame_bytes: 0,
1139 compact_header_target_bytes: 0,
1140 max_side_transport_templates: DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT,
1141 omit_unchanged_compact_timestamps: false,
1142 compact_timestamp_omission_types: CompactTimestampOmissionPolicy::none(),
1143 side_transport_profile: SideTransportProfile::Canonical,
1144 ingress_enabled: true,
1145 egress_enabled: true,
1146 }
1147 }
1148}
1149
1150impl RouterSideOptions {
1151 #[inline]
1156 pub fn with_small_packet_transport(mut self, max_frame_bytes: usize) -> Self {
1157 self.header_template_enabled = true;
1158 self.max_frame_bytes = max_frame_bytes;
1159 self.compact_header_target_bytes = IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES;
1160 self.side_transport_profile = SideTransportProfile::Ipv6Like;
1161 self
1162 }
1163
1164 #[inline]
1165 pub fn with_ipv4_like_compact_header_target(mut self) -> Self {
1166 self.header_template_enabled = true;
1167 self.compact_header_target_bytes = IPV4_LIKE_COMPACT_HEADER_TARGET_BYTES;
1168 self.omit_unchanged_compact_timestamps = true;
1169 self.side_transport_profile = SideTransportProfile::Ipv4Like;
1170 self
1171 }
1172
1173 #[inline]
1174 pub fn with_ipv6_like_compact_header_target(mut self) -> Self {
1175 self.header_template_enabled = true;
1176 self.compact_header_target_bytes = IPV6_LIKE_COMPACT_HEADER_TARGET_BYTES;
1177 self.side_transport_profile = SideTransportProfile::Ipv6Like;
1178 self
1179 }
1180
1181 #[inline]
1182 pub fn with_template_transport(mut self) -> Self {
1183 self.header_template_enabled = true;
1184 self.side_transport_profile = SideTransportProfile::Template;
1185 self
1186 }
1187
1188 #[inline]
1189 pub fn with_omitted_unchanged_compact_timestamps(mut self) -> Self {
1190 self.header_template_enabled = true;
1191 self.omit_unchanged_compact_timestamps = true;
1192 self
1193 }
1194
1195 #[inline]
1196 pub fn with_omitted_unchanged_compact_timestamps_for_type(mut self, ty: DataType) -> Self {
1197 self.header_template_enabled = true;
1198 self.compact_timestamp_omission_types.insert(ty);
1199 self
1200 }
1201
1202 #[inline]
1203 pub fn effective_transport_profile(&self) -> SideTransportProfile {
1204 if !self.header_template_enabled && self.max_frame_bytes == 0 {
1205 SideTransportProfile::Canonical
1206 } else if self.side_transport_profile == SideTransportProfile::Canonical {
1207 SideTransportProfile::Template
1208 } else {
1209 self.side_transport_profile
1210 }
1211 }
1212
1213 #[cfg(feature = "discovery")]
1214 #[inline]
1215 pub fn link_capabilities(&self) -> discovery::LinkCapabilities {
1216 let mut flags = discovery::LINK_CAPABILITY_END_TO_END_RELIABILITY;
1217 if self.header_template_enabled {
1218 flags |= discovery::LINK_CAPABILITY_HEADER_TEMPLATES;
1219 }
1220 if self.max_frame_bytes != 0 {
1221 flags |= discovery::LINK_CAPABILITY_CHUNKING;
1222 }
1223 if self.reliable_enabled {
1224 flags |= discovery::LINK_CAPABILITY_RELIABILITY;
1225 }
1226 if self.omit_unchanged_compact_timestamps
1227 || !self.compact_timestamp_omission_types.is_empty()
1228 {
1229 flags |= discovery::LINK_CAPABILITY_OMIT_UNCHANGED_TIMESTAMPS;
1230 }
1231 #[cfg(feature = "cryptography")]
1232 {
1233 flags |= discovery::LINK_CAPABILITY_CRYPTO;
1234 }
1235 discovery::LinkCapabilities {
1236 version: 1,
1237 flags,
1238 profile: self.effective_transport_profile().discovery_code(),
1239 max_frame_bytes: self.max_frame_bytes.min(u32::MAX as usize) as u32,
1240 compact_header_target_bytes: self.compact_header_target_bytes.min(u32::MAX as usize)
1241 as u32,
1242 max_side_transport_templates: self.max_side_transport_templates.min(u32::MAX as usize)
1243 as u32,
1244 }
1245 }
1246}
1247
1248#[derive(Clone, Debug)]
1250pub struct RouterSide {
1251 pub name: Arc<str>,
1252 pub tx_handler: RouterTxHandlerFn,
1253 pub opts: RouterSideOptions,
1254}
1255
1256impl Debug for EndpointHandler {
1258 #[inline]
1259 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1260 f.debug_struct("EndpointHandler")
1261 .field("endpoint", &self.endpoint)
1262 .field("handler", &self.handler)
1263 .finish()
1264 }
1265}
1266
1267#[inline]
1268pub(crate) const fn endpoint_is_router_internal(endpoint: DataEndpoint) -> bool {
1269 #[cfg(feature = "timesync")]
1270 if matches!(endpoint, DataEndpoint::TimeSync) {
1271 return true;
1272 }
1273 #[cfg(feature = "discovery")]
1274 {
1275 discovery::is_discovery_endpoint(endpoint)
1276 }
1277 #[cfg(not(feature = "discovery"))]
1278 {
1279 let _ = endpoint;
1280 false
1281 }
1282}
1283
1284impl EndpointHandler {
1285 #[inline]
1289 pub fn new_packet_handler<F>(endpoint: DataEndpoint, f: F) -> Self
1290 where
1291 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1292 {
1293 assert!(
1294 !endpoint_is_router_internal(endpoint),
1295 "reserved internal endpoint handlers must not be user-registered"
1296 );
1297 #[cfg(feature = "std")]
1298 crate::config::ensure_endpoint_id(endpoint, false)
1299 .expect("endpoint handler endpoint registration failed");
1300 Self {
1301 endpoint,
1302 handler: EndpointHandlerFn::Packet(Arc::new(f)),
1303 }
1304 }
1305
1306 #[inline]
1308 #[cfg(feature = "std")]
1309 pub fn new_packet_handler_for<F>(endpoint: crate::config::OwnedEndpointDefinition, f: F) -> Self
1310 where
1311 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1312 {
1313 Self::new_packet_handler(endpoint.id, f)
1314 }
1315
1316 #[cfg(not(feature = "std"))]
1318 #[inline]
1319 pub fn new_packet_handler_for<F>(endpoint: crate::config::EndpointDefinition, f: F) -> Self
1320 where
1321 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1322 {
1323 Self::new_packet_handler(endpoint.id, f)
1324 }
1325
1326 #[cfg(feature = "std")]
1328 #[inline]
1329 pub fn new_packet_handler_by_name<F>(endpoint_name: &str, f: F) -> TelemetryResult<Self>
1330 where
1331 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
1332 {
1333 let endpoint = crate::config::endpoint_definition_by_name(endpoint_name)
1334 .ok_or(TelemetryError::BadArg)?;
1335 Ok(Self::new_packet_handler(endpoint.id, f))
1336 }
1337
1338 #[inline]
1342 pub fn new_packed_handler<F>(endpoint: DataEndpoint, f: F) -> Self
1343 where
1344 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
1345 {
1346 assert!(
1347 !endpoint_is_router_internal(endpoint),
1348 "reserved internal endpoint handlers must not be user-registered"
1349 );
1350 #[cfg(feature = "std")]
1351 crate::config::ensure_endpoint_id(endpoint, false)
1352 .expect("endpoint handler endpoint registration failed");
1353 Self {
1354 endpoint,
1355 handler: EndpointHandlerFn::Packed(Arc::new(f)),
1356 }
1357 }
1358
1359 #[inline]
1361 pub fn new_packed_handler_for<F>(endpoint: crate::config::EndpointDefinition, f: F) -> Self
1362 where
1363 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
1364 {
1365 Self::new_packed_handler(endpoint.id, f)
1366 }
1367
1368 #[cfg(feature = "std")]
1370 #[inline]
1371 pub fn new_packed_handler_by_name<F>(endpoint_name: &str, f: F) -> TelemetryResult<Self>
1372 where
1373 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
1374 {
1375 let endpoint = crate::config::endpoint_definition_by_name(endpoint_name)
1376 .ok_or(TelemetryError::BadArg)?;
1377 Ok(Self::new_packed_handler(endpoint.id, f))
1378 }
1379
1380 #[inline]
1382 pub fn get_endpoint(&self) -> DataEndpoint {
1383 self.endpoint
1384 }
1385
1386 #[inline]
1388 pub fn get_handler(&self) -> &EndpointHandlerFn {
1389 &self.handler
1390 }
1391}
1392
1393pub trait Clock {
1394 fn now_ms(&self) -> u64;
1396
1397 fn now_ns(&self) -> u64 {
1401 self.now_ms().saturating_mul(1_000_000)
1402 }
1403}
1404
1405impl<T: Fn() -> u64> Clock for T {
1406 #[inline]
1407 fn now_ms(&self) -> u64 {
1408 self()
1409 }
1410}
1411
1412#[cfg(feature = "std")]
1413#[derive(Debug)]
1414struct StdMonotonicClock {
1415 start: Instant,
1416}
1417
1418#[cfg(feature = "std")]
1419impl Default for StdMonotonicClock {
1420 fn default() -> Self {
1421 Self {
1422 start: Instant::now(),
1423 }
1424 }
1425}
1426
1427#[cfg(feature = "std")]
1428impl Clock for StdMonotonicClock {
1429 fn now_ms(&self) -> u64 {
1430 u64::try_from(self.start.elapsed().as_millis()).unwrap_or(u64::MAX)
1431 }
1432
1433 fn now_ns(&self) -> u64 {
1434 u64::try_from(self.start.elapsed().as_nanos()).unwrap_or(u64::MAX)
1435 }
1436}
1437
1438#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1440pub enum RouterE2eEncryptionMode {
1441 Disabled,
1443 RequiredOnly,
1445 Preferred,
1447 ForceAll,
1449}
1450
1451pub type NodeAddress = u32;
1452pub type P2pPort = u16;
1453pub type P2pStreamId = u32;
1454
1455const P2P_STREAM_MAGIC: [u8; 4] = *b"SDSP";
1456const P2P_STREAM_VERSION: u8 = 1;
1457const P2P_STREAM_SYN: u8 = 0x01;
1458const P2P_STREAM_ACK: u8 = 0x02;
1459const P2P_STREAM_FIN: u8 = 0x04;
1460const P2P_STREAM_RST: u8 = 0x08;
1461const P2P_STREAM_DATA: u8 = 0x10;
1462
1463#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1464pub enum AddressAssignmentMode {
1465 Dynamic,
1466 Requested(NodeAddress),
1467 Static(NodeAddress),
1468}
1469
1470impl AddressAssignmentMode {
1471 #[inline]
1472 fn mode_code(self) -> u8 {
1473 match self {
1474 Self::Dynamic => 0,
1475 Self::Requested(_) => 1,
1476 Self::Static(_) => 2,
1477 }
1478 }
1479
1480 #[inline]
1481 fn requested_address(self) -> NodeAddress {
1482 match self {
1483 Self::Dynamic => 0,
1484 Self::Requested(addr) | Self::Static(addr) => addr,
1485 }
1486 }
1487}
1488
1489#[derive(Debug, Clone, PartialEq, Eq)]
1490pub enum AddressChangeReason {
1491 Configured,
1492 DynamicConflict,
1493 RequestedConflict,
1494 StaticConflict,
1495 HostnameConflict,
1496}
1497
1498#[derive(Debug, Clone, PartialEq, Eq)]
1499pub struct AddressChange {
1500 pub old_hostname: Arc<str>,
1501 pub new_hostname: Arc<str>,
1502 pub old_address: NodeAddress,
1503 pub new_address: NodeAddress,
1504 pub reason: AddressChangeReason,
1505}
1506
1507#[derive(Debug, Clone, PartialEq, Eq)]
1508pub struct AddressBookEntry {
1509 pub hostname: Arc<str>,
1510 pub address: NodeAddress,
1511 pub requested_address: NodeAddress,
1512 pub mode: AddressAssignmentMode,
1513 pub birth_ms: u64,
1514 pub owner_hash: u64,
1515 pub last_seen_ms: u64,
1516}
1517
1518pub struct P2pMessage<'a> {
1519 pub source_hostname: &'a str,
1520 pub source_address: NodeAddress,
1521 pub source_port: P2pPort,
1522 pub destination_port: P2pPort,
1523 pub payload: &'a [u8],
1524}
1525
1526#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1527pub enum P2pStreamEventKind {
1528 Accepted,
1529 Connected,
1530 Data,
1531 Closed,
1532 Reset,
1533}
1534
1535pub struct P2pStreamEvent<'a> {
1536 pub kind: P2pStreamEventKind,
1537 pub stream_id: P2pStreamId,
1538 pub peer_stream_id: P2pStreamId,
1539 pub sequence: u32,
1540 pub peer_hostname: &'a str,
1541 pub peer_address: NodeAddress,
1542 pub local_port: P2pPort,
1543 pub peer_port: P2pPort,
1544 pub payload: &'a [u8],
1545}
1546
1547struct P2pDecoded<'a> {
1548 source_hostname: &'a str,
1549 source_address: NodeAddress,
1550 source_port: P2pPort,
1551 destination_port: P2pPort,
1552 payload: &'a [u8],
1553}
1554
1555#[derive(Debug, Clone)]
1556struct P2pStreamDecoded<'a> {
1557 flags: u8,
1558 source_stream_id: P2pStreamId,
1559 destination_stream_id: P2pStreamId,
1560 sequence: u32,
1561 payload: &'a [u8],
1562}
1563
1564type AddressChangeFn = dyn Fn(AddressChange) -> TelemetryResult<()> + Send + Sync + 'static;
1565
1566#[derive(Clone)]
1567struct AddressChangeHandler {
1568 handler: Arc<AddressChangeFn>,
1569}
1570
1571impl Debug for AddressChangeHandler {
1572 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1573 f.write_str("AddressChangeHandler(<handler>)")
1574 }
1575}
1576
1577type P2pPortHandlerFn = dyn Fn(P2pMessage<'_>) -> TelemetryResult<()> + Send + Sync + 'static;
1578
1579#[derive(Clone)]
1580struct P2pPortHandler {
1581 handler: Arc<P2pPortHandlerFn>,
1582}
1583
1584impl Debug for P2pPortHandler {
1585 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1586 f.write_str("P2pPortHandler(<handler>)")
1587 }
1588}
1589
1590type P2pStreamHandlerFn = dyn Fn(P2pStreamEvent<'_>) -> TelemetryResult<()> + Send + Sync + 'static;
1591
1592#[derive(Clone)]
1593struct P2pStreamHandler {
1594 handler: Arc<P2pStreamHandlerFn>,
1595}
1596
1597impl Debug for P2pStreamHandler {
1598 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1599 f.write_str("P2pStreamHandler(<handler>)")
1600 }
1601}
1602
1603#[derive(Debug, Clone)]
1604struct P2pStreamSession {
1605 peer_hostname: Arc<str>,
1606 peer_address: NodeAddress,
1607 local_port: P2pPort,
1608 peer_port: P2pPort,
1609 peer_stream_id: P2pStreamId,
1610 next_sequence: u32,
1611 connected: bool,
1612}
1613
1614#[derive(Debug, Clone)]
1615struct PendingP2pStreamEvent {
1616 handlers: Vec<P2pStreamHandler>,
1617 kind: P2pStreamEventKind,
1618 stream_id: P2pStreamId,
1619 peer_stream_id: P2pStreamId,
1620 peer_hostname: Arc<str>,
1621 peer_address: NodeAddress,
1622 local_port: P2pPort,
1623 peer_port: P2pPort,
1624}
1625
1626#[derive(Debug, Clone)]
1627pub struct RouterConfig {
1628 handlers: Arc<[EndpointHandler]>,
1630 reliable_enabled: bool,
1632 sender: Option<Arc<str>>,
1634 address_mode: AddressAssignmentMode,
1636 address_change_handlers: Arc<[AddressChangeHandler]>,
1638 e2e_encryption: RouterE2eEncryptionMode,
1640 #[cfg_attr(not(feature = "cryptography"), allow(dead_code))]
1642 e2e_key_id: u32,
1643 memory: RuntimeMemoryConfig,
1644 #[cfg(feature = "discovery")]
1645 preferred_discovery_master: Option<Arc<str>>,
1646 #[cfg(feature = "timesync")]
1647 timesync: Option<TimeSyncConfig>,
1648}
1649
1650impl RouterConfig {
1651 pub fn default_e2e_encryption_mode() -> RouterE2eEncryptionMode {
1656 #[cfg(feature = "cryptography")]
1657 {
1658 RouterE2eEncryptionMode::Preferred
1659 }
1660 #[cfg(not(feature = "cryptography"))]
1661 {
1662 RouterE2eEncryptionMode::Disabled
1663 }
1664 }
1665
1666 pub fn new<H>(handlers: H) -> Self
1668 where
1669 H: Into<Arc<[EndpointHandler]>>,
1670 {
1671 let handlers: Arc<[EndpointHandler]> = handlers.into();
1672 assert!(
1673 handlers
1674 .iter()
1675 .all(|handler| !endpoint_is_router_internal(handler.endpoint)),
1676 "reserved internal endpoint handlers must not be user-registered"
1677 );
1678 Self {
1679 handlers,
1680 reliable_enabled: true,
1681 sender: None,
1682 address_mode: AddressAssignmentMode::Dynamic,
1683 address_change_handlers: Arc::from([]),
1684 e2e_encryption: Self::default_e2e_encryption_mode(),
1685 e2e_key_id: 0,
1686 memory: RuntimeMemoryConfig::default(),
1687 #[cfg(feature = "discovery")]
1688 preferred_discovery_master: None,
1689 #[cfg(feature = "timesync")]
1690 timesync: None,
1691 }
1692 }
1693
1694 pub fn with_reliable_enabled(mut self, enabled: bool) -> Self {
1696 self.reliable_enabled = enabled;
1697 self
1698 }
1699
1700 pub fn with_sender<S: AsRef<str>>(mut self, sender: S) -> Self {
1702 self.sender = Some(Arc::from(sender.as_ref()));
1703 self
1704 }
1705
1706 pub fn with_hostname<S: AsRef<str>>(self, hostname: S) -> Self {
1708 self.with_sender(hostname)
1709 }
1710
1711 pub fn with_dynamic_address(mut self) -> Self {
1713 self.address_mode = AddressAssignmentMode::Dynamic;
1714 self
1715 }
1716
1717 pub fn with_requested_address(mut self, address: NodeAddress) -> Self {
1719 self.address_mode = AddressAssignmentMode::Requested(address);
1720 self
1721 }
1722
1723 pub fn with_static_address(mut self, address: NodeAddress) -> Self {
1725 self.address_mode = AddressAssignmentMode::Static(address);
1726 self
1727 }
1728
1729 pub fn on_address_change<F>(mut self, f: F) -> Self
1731 where
1732 F: Fn(AddressChange) -> TelemetryResult<()> + Send + Sync + 'static,
1733 {
1734 let mut handlers = self.address_change_handlers.to_vec();
1735 handlers.push(AddressChangeHandler {
1736 handler: Arc::new(f),
1737 });
1738 self.address_change_handlers = Arc::from(handlers);
1739 self
1740 }
1741
1742 pub fn with_e2e_encryption(mut self, mode: RouterE2eEncryptionMode) -> Self {
1744 self.e2e_encryption = mode;
1745 self
1746 }
1747
1748 pub fn with_e2e_key_id(mut self, key_id: u32) -> Self {
1750 self.e2e_key_id = key_id;
1751 self
1752 }
1753
1754 pub fn with_memory_config(mut self, memory: RuntimeMemoryConfig) -> TelemetryResult<Self> {
1756 memory.validate()?;
1757 self.memory = memory;
1758 Ok(self)
1759 }
1760
1761 #[cfg(feature = "discovery")]
1767 pub fn with_preferred_discovery_master<S: AsRef<str>>(mut self, hostname: S) -> Self {
1768 let hostname = hostname.as_ref();
1769 self.preferred_discovery_master = if hostname.is_empty() {
1770 None
1771 } else {
1772 Some(Arc::from(hostname))
1773 };
1774 self
1775 }
1776
1777 #[cfg(feature = "timesync")]
1778 pub fn with_timesync(mut self, cfg: TimeSyncConfig) -> Self {
1780 self.timesync = Some(cfg);
1781 self
1782 }
1783
1784 #[inline]
1785 fn is_local_endpoint(&self, ep: DataEndpoint) -> bool {
1787 if endpoint_is_router_internal(ep) {
1788 return false;
1789 }
1790 self.handlers.iter().any(|h| h.endpoint == ep)
1791 }
1792
1793 #[inline]
1794 fn reliable_enabled(&self) -> bool {
1795 self.reliable_enabled
1796 }
1797
1798 #[inline]
1799 fn sender(&self) -> String {
1800 self.sender
1801 .as_deref()
1802 .map(ToString::to_string)
1803 .unwrap_or_else(runtime_device_identifier)
1804 }
1805
1806 #[inline]
1807 fn address_mode(&self) -> AddressAssignmentMode {
1808 self.address_mode
1809 }
1810
1811 #[inline]
1812 fn e2e_encryption(&self) -> RouterE2eEncryptionMode {
1813 self.e2e_encryption
1814 }
1815
1816 #[cfg(feature = "cryptography")]
1817 #[inline]
1818 fn e2e_key_id(&self) -> u32 {
1819 self.e2e_key_id
1820 }
1821
1822 #[cfg(feature = "timesync")]
1823 #[inline]
1824 fn timesync_config(&self) -> Option<TimeSyncConfig> {
1825 self.timesync
1826 }
1827
1828 #[inline]
1829 fn memory_config(&self) -> RuntimeMemoryConfig {
1830 self.memory
1831 }
1832}
1833
1834impl Default for RouterConfig {
1835 fn default() -> Self {
1836 Self {
1837 handlers: Arc::from([]),
1838 reliable_enabled: true,
1839 sender: None,
1840 address_mode: AddressAssignmentMode::Dynamic,
1841 address_change_handlers: Arc::from([]),
1842 e2e_encryption: Self::default_e2e_encryption_mode(),
1843 e2e_key_id: 0,
1844 memory: RuntimeMemoryConfig::default(),
1845 #[cfg(feature = "discovery")]
1846 preferred_discovery_master: None,
1847 #[cfg(feature = "timesync")]
1848 timesync: None,
1849 }
1850 }
1851}
1852
1853pub trait LeBytes: Copy + Sized {
1856 const WIDTH: usize;
1857 fn write_le(self, out: &mut [u8]);
1858 fn from_le_slice(bytes: &[u8]) -> Self;
1859}
1860
1861impl_letype_num!(u8, 1);
1862impl_letype_num!(u16, 2);
1863impl_letype_num!(u32, 4);
1864impl_letype_num!(u64, 8);
1865impl_letype_num!(u128, 16);
1866impl_letype_num!(i8, 1);
1867impl_letype_num!(i16, 2);
1868impl_letype_num!(i32, 4);
1869impl_letype_num!(i64, 8);
1870impl_letype_num!(i128, 16);
1871impl_letype_num!(f32, 4);
1872impl_letype_num!(f64, 8);
1873
1874pub(crate) fn encode_slice_le<T: LeBytes>(data: &[T]) -> Arc<[u8]> {
1876 let total = data.len() * T::WIDTH;
1877 let mut buf = vec![0u8; total];
1878
1879 for (i, v) in data.iter().copied().enumerate() {
1880 let start = i * T::WIDTH;
1881 v.write_le(&mut buf[start..start + T::WIDTH]);
1882 }
1883
1884 Arc::from(buf)
1885}
1886
1887fn make_error_payload(msg: &str) -> crate::SharedBytes {
1890 let meta = message_meta(DataType::TelemetryError);
1891 match meta.element {
1892 MessageElement::Static(_, _, _) => {
1893 let max = get_needed_message_size(DataType::TelemetryError);
1894 let bytes = msg.as_bytes();
1895 let n = core::cmp::min(max, bytes.len());
1896 let mut buf = vec![0u8; max];
1897 if n > 0 {
1898 buf[..n].copy_from_slice(&bytes[..n]);
1899 }
1900 crate::shared_bytes::convert(Arc::<[u8]>::from(buf))
1901 }
1902 MessageElement::Dynamic(_, _) => {
1903 crate::shared_bytes::convert(Arc::<[u8]>::from(msg.as_bytes()))
1904 }
1905 }
1906}
1907
1908fn log_raw<T, F>(
1912 sender: &str,
1913 ty: DataType,
1914 data: &[T],
1915 timestamp: u64,
1916 mut tx_function: F,
1917) -> TelemetryResult<()>
1918where
1919 T: LeBytes,
1920 F: FnMut(Packet) -> TelemetryResult<()>,
1921{
1922 let meta = message_meta(ty);
1923 let got = data.len() * T::WIDTH;
1924
1925 match meta.element {
1926 MessageElement::Static(_, _, _) => {
1927 if got != get_needed_message_size(ty) {
1928 return Err(TelemetryError::SizeMismatch {
1929 expected: get_needed_message_size(ty),
1930 got,
1931 });
1932 }
1933 }
1934 MessageElement::Dynamic(_, _) => {
1935 if !got.is_multiple_of(T::WIDTH) {
1937 return Err(TelemetryError::SizeMismatch {
1938 expected: T::WIDTH,
1939 got,
1940 });
1941 }
1942 }
1943 }
1944
1945 let payload = encode_slice_le(data);
1946 let pkt = Packet::new(ty, meta.endpoints_ref(), sender, timestamp, payload)?;
1947 tx_function(pkt)
1948}
1949
1950#[cfg(feature = "std")]
1952fn reliable_trace_enabled() -> bool {
1953 static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1954 *ENABLED.get_or_init(|| {
1955 matches!(
1956 std::env::var("SEDSNET_TRACE_RELIABLE").as_deref(),
1957 Ok("1" | "true")
1958 )
1959 })
1960}
1961
1962fn fallback_stdout(_msg: &str) {
1966 #[cfg(feature = "std")]
1967 {
1968 eprintln!("{}", _msg);
1969 }
1970 #[cfg(all(not(feature = "std"), target_os = "none"))]
1971 {
1972 let message = format!("{}\n", _msg);
1973 unsafe {
1974 seds_error_msg(message.as_ptr(), message.len());
1975 }
1976 }
1977}
1978
1979#[derive(Debug, Clone)]
1984struct RouterInner {
1985 memory: RuntimeMemoryConfig,
1986 sides: Vec<Option<RouterSide>>,
1987 route_overrides: BTreeMap<(Option<RouterSideId>, RouterSideId), bool>,
1988 typed_route_overrides: BTreeMap<(Option<RouterSideId>, u32, RouterSideId), bool>,
1989 route_weights: BTreeMap<(Option<RouterSideId>, RouterSideId), u32>,
1990 route_priorities: BTreeMap<(Option<RouterSideId>, RouterSideId), u32>,
1991 source_route_modes: BTreeMap<Option<RouterSideId>, RouteSelectionMode>,
1992 route_selection_cursors: BTreeMap<Option<RouterSideId>, u64>,
1993 adaptive_route_stats: BTreeMap<RouterSideId, AdaptiveRouteStats>,
1994 side_runtime_stats: BTreeMap<RouterSideId, SideRuntimeStatsInner>,
1995 side_transport: BTreeMap<RouterSideId, SideTransportState>,
1996 managed_variable_types: BTreeSet<u32>,
1997 managed_variable_permissions: BTreeMap<u32, NetworkVariablePermissions>,
1998 managed_variable_latest: BTreeMap<u32, ManagedVariableCacheEntry>,
1999 network_variable_update_handlers: BTreeMap<u32, Vec<NetworkVariableUpdateHandler>>,
2000 local_address: AddressBookEntry,
2001 address_book: BTreeMap<String, AddressBookEntry>,
2002 address_by_value: BTreeMap<NodeAddress, String>,
2003 p2p_port_handlers: BTreeMap<P2pPort, Vec<P2pPortHandler>>,
2004 p2p_stream_handlers: BTreeMap<P2pPort, Vec<P2pStreamHandler>>,
2005 p2p_stream_sessions: BTreeMap<P2pStreamId, P2pStreamSession>,
2006 next_p2p_stream_id: P2pStreamId,
2007 received_queue: BoundedDeque<RouterRxItem>,
2008 transmit_queue: BoundedDeque<TxQueued>,
2009 tx_priority_burst: u8,
2010 recent_rx: BoundedDeque<u64>,
2011 reliable_tx: BTreeMap<(RouterSideId, u32), ReliableTxState>,
2012 reliable_rx: BTreeMap<(RouterSideId, u32), ReliableRxState>,
2013 reliable_return_routes: BTreeMap<u64, ReliableReturnRouteState>,
2014 reliable_return_route_order: VecDeque<u64>,
2015 end_to_end_reliable_tx: BTreeMap<u64, EndToEndReliableSent>,
2016 end_to_end_reliable_tx_order: VecDeque<u64>,
2017 total_handler_failures: u64,
2018 total_handler_retries: u64,
2019 #[cfg(feature = "discovery")]
2020 discovery_routes: BTreeMap<RouterSideId, DiscoverySideState>,
2021 #[cfg(feature = "discovery")]
2022 discovery_cadence: DiscoveryCadenceState,
2023 #[cfg(feature = "discovery")]
2024 discovery_side_throttle: BTreeMap<RouterSideId, DiscoverySideThrottleState>,
2025 #[cfg(feature = "discovery")]
2026 preferred_discovery_master: Option<Arc<str>>,
2027 #[cfg(all(feature = "discovery", feature = "timesync"))]
2028 timesync_side_throttle: BTreeMap<RouterSideId, TimeSyncSideThrottleState>,
2029}
2030
2031#[derive(Debug, Clone)]
2032struct ManagedVariableCacheEntry {
2033 packet: Packet,
2034 sender_address: u32,
2035 cached_at_ms: u64,
2036}
2037
2038type NetworkVariableUpdateFn = dyn Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static;
2039
2040#[derive(Clone)]
2041struct NetworkVariableUpdateHandler {
2042 handler: Arc<NetworkVariableUpdateFn>,
2043}
2044
2045impl Debug for NetworkVariableUpdateHandler {
2046 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2047 f.write_str("NetworkVariableUpdateHandler(<handler>)")
2048 }
2049}
2050
2051#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2053pub struct NetworkVariablePermissions {
2054 pub read: bool,
2055 pub write: bool,
2056}
2057
2058impl NetworkVariablePermissions {
2059 pub const NONE: Self = Self {
2060 read: false,
2061 write: false,
2062 };
2063 pub const READ_ONLY: Self = Self {
2064 read: true,
2065 write: false,
2066 };
2067 pub const WRITE_ONLY: Self = Self {
2068 read: false,
2069 write: true,
2070 };
2071 pub const READ_WRITE: Self = Self {
2072 read: true,
2073 write: true,
2074 };
2075}
2076
2077#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2078enum RouterQueueKind {
2079 Received,
2080 Transmit,
2081}
2082
2083impl RouterInner {
2084 #[cfg(any(feature = "std", feature = "fair-admission"))]
2085 fn fair_bulk_owner(data: &RouterItem, priority: u8) -> Option<u32> {
2086 match data {
2087 RouterItem::Packet(packet) => crate::admission_fairness::packet_owner(packet, priority),
2088 RouterItem::Packed(bytes) => crate::shared_bytes::with_slice(bytes, |view| {
2089 crate::admission_fairness::packed_owner(view, priority)
2090 })
2091 .ok()
2092 .flatten(),
2093 }
2094 }
2095 #[cfg(any(feature = "std", feature = "fair-admission"))]
2096 fn tx_bulk_owner(item: &TxQueued) -> Option<u32> {
2097 match &item.item {
2098 RouterTxItem::Broadcast(data) | RouterTxItem::ToSide { data, .. } => {
2099 Self::fair_bulk_owner(data, item.priority)
2100 }
2101 _ => None,
2102 }
2103 }
2104 #[cfg(any(feature = "std", feature = "fair-admission"))]
2105 fn bulk_stream(data: &RouterItem) -> Option<u32> {
2106 match data {
2107 RouterItem::Packet(packet) => Some(packet.data_type().as_u32()),
2108 RouterItem::Packed(bytes) => crate::shared_bytes::with_slice(bytes, |view| {
2109 wire_format::scheduling_type(view).map(|ty| ty.as_u32())
2110 })
2111 .ok()
2112 .flatten(),
2113 }
2114 }
2115 #[cfg(any(feature = "std", feature = "fair-admission"))]
2116 fn tx_bulk_stream(item: &TxQueued) -> Option<u32> {
2117 match &item.item {
2118 RouterTxItem::Broadcast(data) | RouterTxItem::ToSide { data, .. } => {
2119 Self::bulk_stream(data)
2120 }
2121 _ => None,
2122 }
2123 }
2124 #[cfg(any(feature = "std", feature = "fair-admission"))]
2125 fn bulk_loads(&self, priority: u8) -> crate::admission_fairness::Loads {
2126 let mut loads = crate::admission_fairness::Loads::new();
2127 for item in self.received_queue.iter() {
2128 if item.priority <= priority
2129 && let Some(owner) = Self::fair_bulk_owner(&item.data, item.priority)
2130 {
2131 loads.record(owner, item.byte_cost());
2132 }
2133 }
2134 for item in self.transmit_queue.iter() {
2135 if item.priority <= priority
2136 && let Some(owner) = Self::tx_bulk_owner(item)
2137 {
2138 loads.record(owner, item.byte_cost());
2139 }
2140 }
2141 loads
2142 }
2143 #[cfg(any(feature = "std", feature = "fair-admission"))]
2144 fn competing_bulk_owner(&self, incoming: u32, priority: u8) -> Option<u32> {
2145 self.bulk_loads(priority).dominant_other(incoming)
2146 }
2147 #[cfg(any(feature = "std", feature = "fair-admission"))]
2148 fn reclaim_bulk_from(
2149 &mut self,
2150 owner: u32,
2151 priority: u8,
2152 incoming_stream: Option<u32>,
2153 ) -> bool {
2154 let mut streams = crate::admission_fairness::Loads::new();
2155 for item in self.received_queue.iter() {
2156 if item.priority <= priority
2157 && Self::fair_bulk_owner(&item.data, item.priority) == Some(owner)
2158 && let Some(ty) = Self::bulk_stream(&item.data)
2159 {
2160 streams.record(ty, item.byte_cost());
2161 }
2162 }
2163 for item in self.transmit_queue.iter() {
2164 if item.priority <= priority
2165 && Self::tx_bulk_owner(item) == Some(owner)
2166 && let Some(ty) = Self::tx_bulk_stream(item)
2167 {
2168 streams.record(ty, item.byte_cost());
2169 }
2170 }
2171 let stream = streams.queue_victim(incoming_stream);
2172 let rx = self
2173 .received_queue
2174 .iter()
2175 .enumerate()
2176 .filter(|(_, item)| {
2177 item.priority <= priority
2178 && Self::fair_bulk_owner(&item.data, item.priority) == Some(owner)
2179 && stream.is_none_or(|ty| Self::bulk_stream(&item.data) == Some(ty))
2180 })
2181 .min_by_key(|(index, item)| (item.priority, *index))
2182 .map(|(index, item)| (index, item.priority));
2183 let tx = self
2184 .transmit_queue
2185 .iter()
2186 .enumerate()
2187 .filter(|(_, item)| {
2188 item.priority <= priority
2189 && Self::tx_bulk_owner(item) == Some(owner)
2190 && stream.is_none_or(|ty| Self::tx_bulk_stream(item) == Some(ty))
2191 })
2192 .min_by_key(|(index, item)| (item.priority, *index))
2193 .map(|(index, item)| (index, item.priority));
2194 match (rx, tx) {
2195 (Some((index, rp)), Some((_, tp))) if rp <= tp => {
2196 self.received_queue.remove_pos(index).is_some()
2197 }
2198 (Some((index, _)), None) => self.received_queue.remove_pos(index).is_some(),
2199 (_, Some((index, _))) => self.transmit_queue.remove_pos(index).is_some(),
2200 _ => false,
2201 }
2202 }
2203
2204 #[cfg(feature = "discovery")]
2205 fn topology_board_byte_cost(board: &TopologyBoardNode) -> usize {
2206 board
2207 .sender_id
2208 .len()
2209 .saturating_add(board.reachable_endpoints.len() * size_of::<DataEndpoint>())
2210 .saturating_add(
2211 board
2212 .reachable_timesync_sources
2213 .iter()
2214 .map(|s| s.len())
2215 .sum::<usize>(),
2216 )
2217 .saturating_add(board.connections.iter().map(|s| s.len()).sum::<usize>())
2218 }
2219
2220 #[cfg(feature = "discovery")]
2221 fn discovery_sender_byte_cost(sender: &str, state: &DiscoverySenderState) -> usize {
2222 sender
2223 .len()
2224 .saturating_add(state.reachable.len() * size_of::<DataEndpoint>())
2225 .saturating_add(state.advertised_reachable.len() * size_of::<DataEndpoint>())
2226 .saturating_add(state.reachable_network_variables.len() * size_of::<DataType>())
2227 .saturating_add(
2228 state
2229 .reachable_timesync_sources
2230 .iter()
2231 .map(|s| s.len())
2232 .sum::<usize>(),
2233 )
2234 .saturating_add(
2235 state
2236 .advertised_reachable_timesync_sources
2237 .iter()
2238 .map(|s| s.len())
2239 .sum::<usize>(),
2240 )
2241 .saturating_add(
2242 state
2243 .topology_boards
2244 .iter()
2245 .map(Self::topology_board_byte_cost)
2246 .sum::<usize>(),
2247 )
2248 .saturating_add(size_of::<DiscoverySenderState>())
2249 }
2250
2251 #[cfg(feature = "discovery")]
2252 fn discovery_route_byte_cost(side: RouterSideId, route: &DiscoverySideState) -> usize {
2253 size_of::<RouterSideId>()
2254 .saturating_add(size_of::<DiscoverySideState>())
2255 .saturating_add(route.reachable.len() * size_of::<DataEndpoint>())
2256 .saturating_add(route.reachable_network_variables.len() * size_of::<DataType>())
2257 .saturating_add(
2258 route.requested_network_variables.len()
2259 * (size_of::<DataType>() + size_of::<u64>()),
2260 )
2261 .saturating_add(
2262 route
2263 .reachable_timesync_sources
2264 .iter()
2265 .map(|s| s.len())
2266 .sum::<usize>(),
2267 )
2268 .saturating_add(
2269 route
2270 .announcers
2271 .iter()
2272 .map(|(sender, state)| Self::discovery_sender_byte_cost(sender, state))
2273 .sum::<usize>(),
2274 )
2275 .saturating_add(side.saturating_sub(side))
2276 }
2277
2278 #[cfg(feature = "discovery")]
2279 fn discovery_bytes_used(&self) -> usize {
2280 self.discovery_routes
2281 .iter()
2282 .map(|(side, route)| Self::discovery_route_byte_cost(*side, route))
2283 .sum()
2284 }
2285
2286 #[inline]
2287 fn reliable_rx_buffered_bytes(&self) -> usize {
2288 self.reliable_rx
2289 .values()
2290 .flat_map(|state| state.buffered.values())
2291 .map(|bytes| size_of::<crate::SharedBytes>() + bytes.len())
2292 .sum()
2293 }
2294
2295 fn reliable_rx_buffer_len(&self) -> usize {
2296 self.reliable_rx
2297 .values()
2298 .map(|state| state.buffered.len())
2299 .sum()
2300 }
2301
2302 #[inline]
2303 fn shared_queue_bytes_used(&self) -> usize {
2304 self.received_queue
2305 .bytes_used()
2306 .saturating_add(self.transmit_queue.bytes_used())
2307 .saturating_add(self.recent_rx.max_bytes())
2308 .saturating_add(self.reliable_rx_buffered_bytes())
2309 .saturating_add(crate::config::schema_bytes_used())
2310 .saturating_add({
2311 #[cfg(feature = "discovery")]
2312 {
2313 self.discovery_bytes_used()
2314 }
2315 #[cfg(not(feature = "discovery"))]
2316 {
2317 0
2318 }
2319 })
2320 }
2321
2322 fn pop_shared_queue_item(&mut self, preferred: RouterQueueKind) -> bool {
2323 match preferred {
2324 RouterQueueKind::Received => self.received_queue.pop_lowest_priority().is_some(),
2329 RouterQueueKind::Transmit => self.transmit_queue.pop_lowest_priority().is_some(),
2330 }
2331 }
2332
2333 fn lower_priority_shared_queue(&self, incoming_priority: u8) -> Option<RouterQueueKind> {
2334 let received = self
2335 .received_queue
2336 .lowest_priority(|queued| queued.priority)
2337 .map(|priority| (RouterQueueKind::Received, priority));
2338 let transmit = self
2339 .transmit_queue
2340 .lowest_priority(|queued| queued.priority)
2341 .map(|priority| (RouterQueueKind::Transmit, priority));
2342 [received, transmit]
2343 .into_iter()
2344 .flatten()
2345 .filter(|(_, priority)| {
2346 *priority < incoming_priority
2347 || (incoming_priority != 254 && *priority == incoming_priority)
2348 })
2349 .min_by_key(|(kind, priority)| {
2350 (
2351 *priority,
2352 if *kind == RouterQueueKind::Received {
2353 0
2354 } else {
2355 1
2356 },
2357 )
2358 })
2359 .map(|(kind, _)| kind)
2360 }
2361
2362 fn make_shared_queue_room(
2363 &mut self,
2364 incoming_cost: usize,
2365 incoming_priority: u8,
2366 _incoming: Option<&RouterItem>,
2367 ) -> TelemetryResult<()> {
2368 if incoming_cost > self.memory.max_queue_budget {
2369 return Err(TelemetryError::PacketTooLarge(
2370 "Item exceeds maximum shared queue budget",
2371 ));
2372 }
2373
2374 #[cfg(any(feature = "std", feature = "fair-admission"))]
2375 let mut fair_victim = None;
2376 while self.shared_queue_bytes_used().saturating_add(incoming_cost)
2377 > self.memory.max_queue_budget
2378 {
2379 #[cfg(any(feature = "std", feature = "fair-admission"))]
2380 {
2381 let incoming =
2382 _incoming.and_then(|data| Self::fair_bulk_owner(data, incoming_priority));
2383 let victim = *fair_victim.get_or_insert_with(|| {
2384 self.bulk_loads(incoming_priority.min(199))
2385 .queue_victim(incoming)
2386 });
2387 if let Some(victim) = victim
2388 && self.reclaim_bulk_from(
2389 victim,
2390 incoming_priority.min(199),
2391 (incoming == Some(victim))
2392 .then(|| _incoming.and_then(Self::bulk_stream))
2393 .flatten(),
2394 )
2395 {
2396 continue;
2397 }
2398 if incoming.is_some() {
2399 return Err(TelemetryError::Io("shared fair queue saturated"));
2400 }
2401 }
2402 let Some(victim) = self.lower_priority_shared_queue(incoming_priority) else {
2403 return Err(TelemetryError::Io("shared priority queue saturated"));
2404 };
2405 if !self.pop_shared_queue_item(victim) {
2406 return Err(TelemetryError::PacketTooLarge(
2407 "Item exceeds maximum shared queue budget",
2408 ));
2409 }
2410 }
2411
2412 Ok(())
2413 }
2414
2415 #[cfg(feature = "discovery")]
2416 fn schema_growth_budget(&self) -> usize {
2417 let protected = self
2420 .recent_rx
2421 .max_bytes()
2422 .saturating_add(self.discovery_bytes_used())
2423 .saturating_add(self.reliable_rx_buffered_bytes());
2424 self.memory.max_queue_budget.saturating_sub(protected)
2425 }
2426
2427 #[cfg(feature = "discovery")]
2428 fn commit_discovery_route(
2429 &mut self,
2430 side: RouterSideId,
2431 route: DiscoverySideState,
2432 ) -> TelemetryResult<()> {
2433 let previous = self
2434 .discovery_routes
2435 .get(&side)
2436 .map_or(0, |route| Self::discovery_route_byte_cost(side, route));
2437 let incoming = Self::discovery_route_byte_cost(side, &route);
2438 let growth = incoming.saturating_sub(previous);
2439 self.make_shared_queue_room(growth, 255, None)?;
2440 self.discovery_routes.insert(side, route);
2441 Ok(())
2442 }
2443
2444 #[cfg(feature = "discovery")]
2445 fn fit_discovery_budget(&mut self) {
2446 while self.shared_queue_bytes_used() > self.memory.max_queue_budget {
2447 if let Some(victim) = self.lower_priority_shared_queue(255)
2450 && self.pop_shared_queue_item(victim)
2451 {
2452 continue;
2453 }
2454 break;
2457 }
2458 }
2459
2460 fn push_received(&mut self, item: RouterRxItem) -> TelemetryResult<()> {
2461 self.make_shared_queue_room(item.byte_cost(), item.priority, Some(&item.data))?;
2462 #[cfg(any(feature = "std", feature = "fair-admission"))]
2463 {
2464 #[cfg(any(feature = "std", feature = "fair-admission"))]
2465 let mut fair_victim = None;
2466 self.received_queue.push_back_prioritized_select(
2467 item,
2468 |queued| queued.priority,
2469 |queue, _incoming| {
2470 #[cfg(any(feature = "std", feature = "fair-admission"))]
2471 {
2472 crate::admission_fairness::queue_victim(
2473 queue,
2474 _incoming,
2475 |item| Self::fair_bulk_owner(&item.data, item.priority),
2476 |item| item.priority,
2477 |item| Self::bulk_stream(&item.data),
2478 &mut fair_victim,
2479 )
2480 }
2481 },
2482 )
2483 }
2484 #[cfg(not(any(feature = "std", feature = "fair-admission")))]
2485 {
2486 self.received_queue
2487 .push_back_prioritized(item, |queued| queued.priority)
2488 }
2489 }
2490
2491 fn push_transmit(&mut self, item: TxQueued) -> TelemetryResult<()> {
2492 #[cfg(feature = "discovery")]
2493 if let RouterTxItem::ToSide { dst, data, .. } = &item.item {
2494 if let RouterItem::Packet(incoming) = data
2497 && self.transmit_queue.iter().any(|queued| {
2498 matches!(&queued.item,
2499 RouterTxItem::ToSide { dst: queued_dst, data: RouterItem::Packet(existing), .. }
2500 if queued_dst == dst && discovery::same_schema_snapshot(existing, incoming))
2501 })
2502 {
2503 return Ok(());
2504 }
2505 let incoming = match data {
2506 RouterItem::Packet(pkt) if discovery::is_discovery_type(pkt.data_type()) => {
2507 Some((pkt.data_type(), pkt.sender().to_owned()))
2508 }
2509 RouterItem::Packet(_) | RouterItem::Packed(_) => None,
2510 };
2511 if let Some((incoming_ty, incoming_sender)) = incoming {
2512 self.transmit_queue.retain(|queued| {
2513 let RouterTxItem::ToSide {
2514 dst: queued_dst,
2515 data: queued_data,
2516 ..
2517 } = &queued.item
2518 else {
2519 return true;
2520 };
2521 let same_snapshot = match queued_data {
2522 RouterItem::Packet(pkt) => {
2523 pkt.data_type() == incoming_ty && pkt.sender() == incoming_sender
2524 }
2525 RouterItem::Packed(_) => false,
2526 };
2527 *queued_dst != *dst || !same_snapshot
2528 });
2529 }
2530 }
2531 self.make_shared_queue_room(
2532 item.byte_cost(),
2533 item.priority,
2534 match &item.item {
2535 RouterTxItem::Broadcast(data) | RouterTxItem::ToSide { data, .. } => Some(data),
2536 _ => None,
2537 },
2538 )?;
2539 #[cfg(any(feature = "std", feature = "fair-admission"))]
2540 {
2541 #[cfg(any(feature = "std", feature = "fair-admission"))]
2542 let mut fair_victim = None;
2543 self.transmit_queue.push_back_prioritized_select(
2544 item,
2545 |queued| queued.priority,
2546 |queue, _incoming| {
2547 #[cfg(any(feature = "std", feature = "fair-admission"))]
2548 {
2549 crate::admission_fairness::queue_victim(
2550 queue,
2551 _incoming,
2552 Self::tx_bulk_owner,
2553 |item| item.priority,
2554 Self::tx_bulk_stream,
2555 &mut fair_victim,
2556 )
2557 }
2558 },
2559 )
2560 }
2561 #[cfg(not(any(feature = "std", feature = "fair-admission")))]
2562 {
2563 self.transmit_queue
2564 .push_back_prioritized(item, |queued| queued.priority)
2565 }
2566 }
2567
2568 fn push_recent_rx(&mut self, id: u64) -> TelemetryResult<()> {
2569 while self.recent_rx.len() >= self.memory.max_recent_rx_ids {
2570 let _ = self.recent_rx.pop_front();
2571 }
2572 self.make_shared_queue_room(0, 0, None)?;
2573 self.recent_rx.push_back(id)
2574 }
2575
2576 #[allow(unused_mut)]
2577 fn buffer_reliable_rx(
2578 &mut self,
2579 side: RouterSideId,
2580 ty: DataType,
2581 seq: u32,
2582 mut bytes: crate::SharedBytes,
2583 ) -> TelemetryResult<()> {
2584 let key = Router::reliable_key(side, ty);
2585 if self
2586 .reliable_rx
2587 .get(&key)
2588 .is_some_and(|state| state.buffered.contains_key(&seq))
2589 {
2590 return Ok(());
2591 }
2592 let cost = size_of::<crate::SharedBytes>() + bytes.len();
2593 self.make_shared_queue_room(cost, crate::transport_priority(ty), None)?;
2594 #[cfg(feature = "compact-packet-store")]
2595 bytes.park_for_queue()?;
2596 let rx_state = self
2597 .reliable_rx
2598 .entry(key)
2599 .or_insert_with(|| ReliableRxState {
2600 expected_seq: 1,
2601 buffered: BTreeMap::new(),
2602 });
2603 if rx_state.buffered.len() >= runtime_reliable_max_pending() {
2604 let _ = rx_state.buffered.pop_first();
2605 }
2606 rx_state.buffered.insert(seq, bytes);
2607 Ok(())
2608 }
2609}
2610
2611struct IsrRxQueue {
2616 busy: AtomicBool,
2617 q: UnsafeCell<BoundedDeque<RouterRxItem>>,
2618}
2619
2620unsafe impl Send for IsrRxQueue {}
2621unsafe impl Sync for IsrRxQueue {}
2622
2623struct IsrRxQueueGuard<'a> {
2624 owner: &'a IsrRxQueue,
2625}
2626
2627impl Deref for IsrRxQueueGuard<'_> {
2628 type Target = BoundedDeque<RouterRxItem>;
2629
2630 #[inline]
2631 fn deref(&self) -> &Self::Target {
2632 unsafe { &*self.owner.q.get() }
2633 }
2634}
2635
2636impl DerefMut for IsrRxQueueGuard<'_> {
2637 #[inline]
2638 fn deref_mut(&mut self) -> &mut Self::Target {
2639 unsafe { &mut *self.owner.q.get() }
2640 }
2641}
2642
2643impl Drop for IsrRxQueueGuard<'_> {
2644 #[inline]
2645 fn drop(&mut self) {
2646 self.owner.busy.store(false, Ordering::Release);
2647 }
2648}
2649
2650impl IsrRxQueue {
2651 #[inline]
2652 fn new(max_bytes: usize, starting_bytes: usize, grow_mult: f64) -> Self {
2653 Self {
2654 busy: AtomicBool::new(false),
2655 q: UnsafeCell::new(BoundedDeque::new(max_bytes, starting_bytes, grow_mult)),
2656 }
2657 }
2658
2659 #[inline]
2660 fn try_lock(&self) -> TelemetryResult<IsrRxQueueGuard<'_>> {
2661 match self
2662 .busy
2663 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
2664 {
2665 Ok(_) => Ok(IsrRxQueueGuard { owner: self }),
2666 Err(_) => Err(TelemetryError::Io("rx queue busy")),
2667 }
2668 }
2669
2670 #[allow(dead_code)]
2671 #[inline]
2672 fn push_back(&self, item: RouterRxItem) -> TelemetryResult<()> {
2673 let mut g = self.try_lock()?;
2674 g.push_back(item)
2675 }
2676
2677 #[inline]
2678 fn push_back_prioritized(&self, item: RouterRxItem) -> TelemetryResult<()> {
2679 let mut g = self.try_lock()?;
2680 g.push_back_prioritized(item, |queued| queued.priority)
2681 }
2682
2683 #[inline]
2684 fn pop_front(&self) -> TelemetryResult<Option<RouterRxItem>> {
2685 let mut g = self.try_lock()?;
2686 Ok(g.pop_front())
2687 }
2688
2689 #[inline]
2690 fn clear(&self) -> TelemetryResult<()> {
2691 let mut g = self.try_lock()?;
2692 g.clear();
2693 Ok(())
2694 }
2695
2696 #[inline]
2697 fn snapshot(&self) -> Option<(usize, usize)> {
2698 let g = self.try_lock().ok()?;
2699 Some((g.len(), g.bytes_used()))
2700 }
2701}
2702
2703pub struct Router {
2707 sender: RouterMutex<Arc<str>>,
2708 cfg: RouterConfig,
2709 state: RouterMutex<RouterInner>,
2710 isr_rx_queue: IsrRxQueue,
2711 side_tx_gate: ReentryGate,
2712 clock: Box<dyn Clock + Send + Sync>,
2713 #[cfg(feature = "timesync")]
2714 timesync: RouterMutex<TimeSyncRuntime>,
2715}
2716
2717#[cfg(feature = "timesync")]
2718#[derive(Debug, Clone)]
2719struct PendingTimeSyncRequest {
2720 seq: u64,
2721 t1_mono_ms: u64,
2722 source: String,
2723}
2724
2725#[cfg(feature = "timesync")]
2726#[derive(Debug, Clone)]
2727struct RemoteTimeSyncSource {
2728 priority: u64,
2729 last_sample_mono_ms: u64,
2730 sample_unix_ms: u64,
2731}
2732
2733#[cfg(feature = "timesync")]
2734#[derive(Debug, Clone)]
2735struct TimeSyncRuntime {
2736 cfg: Option<TimeSyncConfig>,
2737 tracker: Option<TimeSyncTracker>,
2738 clock: NetworkClock,
2739 disciplined_clock: SlewedNetworkClock,
2740 remote_sources: BTreeMap<String, RemoteTimeSyncSource>,
2741 next_seq: u64,
2742 next_announce_mono_ms: u64,
2743 next_request_mono_ms: u64,
2744 pending_request: Option<PendingTimeSyncRequest>,
2745}
2746
2747#[cfg(feature = "timesync")]
2748impl TimeSyncRuntime {
2749 fn new(cfg: Option<TimeSyncConfig>) -> Self {
2750 Self {
2751 tracker: cfg.map(TimeSyncTracker::new),
2752 cfg,
2753 clock: NetworkClock::default(),
2754 disciplined_clock: SlewedNetworkClock::new(
2755 cfg.map(|c| c.max_slew_ppm)
2756 .unwrap_or(TimeSyncConfig::default().max_slew_ppm),
2757 ),
2758 remote_sources: BTreeMap::new(),
2759 next_seq: 1,
2760 next_announce_mono_ms: 0,
2761 next_request_mono_ms: 0,
2762 pending_request: None,
2763 }
2764 }
2765}
2766
2767enum RemoteSidePlan {
2768 Target(Vec<RouterSideId>),
2769}
2770
2771#[cfg(feature = "discovery")]
2772#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
2773struct DiscoveryCandidateMatch {
2774 side: RouterSideId,
2775 overlap: usize,
2776}
2777
2778#[cfg(feature = "discovery")]
2779#[inline]
2780const fn discovery_is_hop_local_advertisement(ty: DataType) -> bool {
2781 matches!(
2782 ty,
2783 DataType::DiscoveryAnnounce
2784 | DataType::DiscoveryTimeSyncSources
2785 | DataType::DiscoveryTopology
2786 | DataType::DiscoveryAddress
2787 | DataType::DiscoveryLinkCapabilities
2788 )
2789}
2790
2791#[cfg(not(feature = "discovery"))]
2792const fn discovery_is_hop_local_advertisement(_ty: DataType) -> bool {
2793 false
2794}
2795
2796impl Debug for Router {
2797 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2798 let sender = self.sender();
2799 f.debug_struct("Router")
2800 .field("sender", &sender)
2801 .field("cfg", &self.cfg)
2802 .field("state", &"<mutex>")
2803 .field("clock", &"Clock")
2804 .finish()
2805 }
2806}
2807
2808#[inline]
2811fn has_nonlocal_endpoint(eps: &[DataEndpoint], cfg: &RouterConfig) -> bool {
2812 eps.iter().copied().any(|ep| !cfg.is_local_endpoint(ep))
2813}
2814
2815#[inline]
2816fn force_remote_for_type(ty: DataType) -> bool {
2817 matches!(
2818 ty,
2819 DataType::ReliableAck
2820 | DataType::ReliablePartialAck
2821 | DataType::ReliablePacketRequest
2822 | DataType::P2pMessage
2823 ) || {
2824 #[cfg(feature = "timesync")]
2825 {
2826 matches!(
2827 ty,
2828 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
2829 )
2830 }
2831 #[cfg(not(feature = "timesync"))]
2832 {
2833 false
2834 }
2835 }
2836}
2837
2838#[inline]
2839fn is_internal_control_type(ty: DataType) -> bool {
2840 if matches!(
2841 ty,
2842 DataType::ReliableAck
2843 | DataType::ReliablePartialAck
2844 | DataType::ReliablePacketRequest
2845 | DataType::P2pMessage
2846 ) {
2847 return true;
2848 }
2849
2850 #[cfg(feature = "timesync")]
2851 if matches!(
2852 ty,
2853 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
2854 ) {
2855 return true;
2856 }
2857
2858 #[cfg(feature = "discovery")]
2859 if discovery::is_discovery_type(ty) {
2860 return true;
2861 }
2862
2863 let _ = ty;
2864 false
2865}
2866
2867fn with_retries<F>(
2869 this: &Router,
2870 dest: DataEndpoint,
2871 data: &RouterItem,
2872 pkt_for_ctx: Option<&Packet>,
2873 env_for_ctx: Option<&wire_format::TelemetryEnvelope>,
2874 called_from_queue: bool,
2875 run: F,
2876) -> TelemetryResult<()>
2877where
2878 F: Fn() -> TelemetryResult<()>,
2879{
2880 match this.retry_with_attempts(runtime_max_handler_retries(), run) {
2881 Ok(((), attempts)) => {
2882 if attempts > 1 {
2883 let mut st = this.state.lock();
2884 st.total_handler_retries = st
2885 .total_handler_retries
2886 .saturating_add(attempts.saturating_sub(1) as u64);
2887 }
2888 Ok(())
2889 }
2890 Err((e, attempts)) => {
2891 {
2892 let mut st = this.state.lock();
2893 st.total_handler_failures = st.total_handler_failures.saturating_add(1);
2894 st.total_handler_retries = st.total_handler_retries.saturating_add(attempts as u64);
2895 }
2896
2897 this.remove_pkt_id(data);
2899
2900 if let Some(pkt) = pkt_for_ctx {
2902 let _ = this.handle_callback_error(pkt, Some(dest), e, called_from_queue);
2903 } else if let Some(env) = env_for_ctx {
2904 let _ = this.handle_callback_error_from_env(env, Some(dest), e, called_from_queue);
2905 }
2906
2907 Err(TelemetryError::HandlerError("local handler failed"))
2908 }
2909 }
2910}
2911impl Router {
2913 const END_TO_END_ACK_SENDER: &'static str = "E2EACK";
2914 const END_TO_END_ACK_PREFIX: &'static str = "E2EACK:";
2915
2916 #[inline]
2917 fn side_ref(st: &RouterInner, side: RouterSideId) -> TelemetryResult<&RouterSide> {
2918 st.sides
2919 .get(side)
2920 .and_then(|side| side.as_ref())
2921 .ok_or(TelemetryError::HandlerError("router: invalid side id"))
2922 }
2923
2924 fn note_side_tx_success(
2925 &self,
2926 side: RouterSideId,
2927 ty: DataType,
2928 bytes: usize,
2929 relayed: bool,
2930 attempts: usize,
2931 ) {
2932 let mut st = self.state.lock();
2933 #[cfg(feature = "discovery")]
2934 if ty == crate::DataType::DiscoveryAnnounce
2935 && !relayed
2936 && let Some(throttle) = st.discovery_side_throttle.get_mut(&side)
2937 {
2938 throttle.last_liveness_tx_ms = self.clock.now_ms();
2939 }
2940 let entry = st.side_runtime_stats.entry(side).or_default();
2941 entry.note_tx(ty, bytes, relayed, attempts.saturating_sub(1));
2942 }
2943
2944 fn note_side_tx_failure(&self, side: RouterSideId, ty: DataType, attempts: usize) {
2945 let mut st = self.state.lock();
2946 st.total_handler_failures = st.total_handler_failures.saturating_add(1);
2947 st.total_handler_retries = st.total_handler_retries.saturating_add(attempts as u64);
2948 let entry = st.side_runtime_stats.entry(side).or_default();
2949 entry.note_tx_failure(ty, attempts);
2950 }
2951
2952 fn note_side_rx(&self, side: RouterSideId, ty: DataType, bytes: usize, relayed: bool) {
2953 let mut st = self.state.lock();
2954 let entry = st.side_runtime_stats.entry(side).or_default();
2955 entry.note_rx(ty, bytes, relayed);
2956 }
2957
2958 fn note_side_local_delivery(&self, side: RouterSideId, ty: DataType) {
2959 let mut st = self.state.lock();
2960 let entry = st.side_runtime_stats.entry(side).or_default();
2961 entry.note_local_delivery(ty);
2962 }
2963
2964 fn note_side_local_handler_failure(&self, side: RouterSideId, ty: DataType, retries: usize) {
2965 let mut st = self.state.lock();
2966 let entry = st.side_runtime_stats.entry(side).or_default();
2967 entry.note_local_handler_failure(ty, retries);
2968 }
2969
2970 fn cache_managed_variable_packet(
2971 &self,
2972 pkt: &Packet,
2973 notify_handlers: bool,
2974 ) -> TelemetryResult<()> {
2975 let sender_address = pkt
2976 .sender()
2977 .strip_prefix("@addr:")
2978 .and_then(|value| value.parse::<u32>().ok())
2979 .unwrap_or_else(|| sender_address_u32(pkt.sender()));
2980 let handlers = {
2981 let mut st = self.state.lock();
2982 if !st
2983 .managed_variable_types
2984 .contains(&pkt.data_type().as_u32())
2985 {
2986 return Ok(());
2987 }
2988 let existing = st.managed_variable_latest.get(&pkt.data_type().as_u32());
2989 if existing.is_some_and(|entry| {
2996 let current = &entry.packet;
2997 sender_address == entry.sender_address && pkt.timestamp() < current.timestamp()
2998 }) {
2999 return Ok(());
3000 }
3001 let changed = existing.is_none_or(|entry| entry.packet != *pkt);
3002 st.managed_variable_latest.insert(
3003 pkt.data_type().as_u32(),
3004 ManagedVariableCacheEntry {
3005 packet: pkt.clone(),
3006 sender_address,
3007 cached_at_ms: self.clock.now_ms(),
3008 },
3009 );
3010 if notify_handlers && changed {
3011 st.network_variable_update_handlers
3012 .get(&pkt.data_type().as_u32())
3013 .cloned()
3014 .unwrap_or_default()
3015 } else {
3016 Vec::new()
3017 }
3018 };
3019 for handler in handlers {
3020 (handler.handler)(pkt)?;
3021 }
3022 Ok(())
3023 }
3024
3025 fn remember_managed_variable_packet(&self, pkt: &Packet) -> TelemetryResult<()> {
3026 self.cache_managed_variable_packet(pkt, true)
3027 }
3028
3029 fn managed_variable_latest(&self, ty: DataType) -> Option<Packet> {
3030 let st = self.state.lock();
3031 st.managed_variable_latest
3032 .get(&ty.as_u32())
3033 .map(|entry| entry.packet.clone())
3034 }
3035
3036 fn managed_variable_latest_with_age(&self, ty: DataType) -> Option<(Packet, u64)> {
3037 let now_ms = self.clock.now_ms();
3038 let st = self.state.lock();
3039 st.managed_variable_latest.get(&ty.as_u32()).map(|entry| {
3040 (
3041 entry.packet.clone(),
3042 now_ms.saturating_sub(entry.cached_at_ms),
3043 )
3044 })
3045 }
3046
3047 fn is_managed_variable_type(&self, ty: DataType) -> bool {
3048 let st = self.state.lock();
3049 st.managed_variable_types.contains(&ty.as_u32())
3050 }
3051
3052 fn managed_variable_permissions_locked(
3053 st: &RouterInner,
3054 ty: DataType,
3055 ) -> NetworkVariablePermissions {
3056 st.managed_variable_permissions
3057 .get(&ty.as_u32())
3058 .copied()
3059 .unwrap_or(NetworkVariablePermissions::READ_WRITE)
3060 }
3061
3062 fn can_read_managed_variable(&self, ty: DataType) -> bool {
3063 let st = self.state.lock();
3064 Self::managed_variable_permissions_locked(&st, ty).read
3065 }
3066
3067 fn can_write_managed_variable(&self, ty: DataType) -> bool {
3068 let st = self.state.lock();
3069 Self::managed_variable_permissions_locked(&st, ty).write
3070 }
3071
3072 #[inline]
3073 fn ensure_side_ingress_enabled(&self, side: RouterSideId) -> TelemetryResult<()> {
3074 let st = self.state.lock();
3075 let side_ref = Self::side_ref(&st, side)?;
3076 if side_ref.opts.ingress_enabled {
3077 Ok(())
3078 } else {
3079 Err(TelemetryError::HandlerError(
3080 "router: ingress disabled for side id",
3081 ))
3082 }
3083 }
3084
3085 #[inline]
3086 fn default_route_enabled(&self, src: Option<RouterSideId>, dst: RouterSideId) -> bool {
3087 src != Some(dst)
3088 }
3089
3090 #[inline]
3091 fn route_allowed_locked(
3092 &self,
3093 st: &RouterInner,
3094 src: Option<RouterSideId>,
3095 ty: Option<DataType>,
3096 dst: RouterSideId,
3097 ) -> bool {
3098 let Some(dst_side) = st.sides.get(dst).and_then(|side| side.as_ref()) else {
3099 return false;
3100 };
3101 if !dst_side.opts.egress_enabled {
3102 return false;
3103 }
3104 if let Some(src_id) = src {
3105 let Some(src_side) = st.sides.get(src_id).and_then(|side| side.as_ref()) else {
3106 return false;
3107 };
3108 if !src_side.opts.ingress_enabled {
3109 return false;
3110 }
3111 }
3112 let base_allowed = st
3113 .route_overrides
3114 .get(&(src, dst))
3115 .copied()
3116 .unwrap_or_else(|| self.default_route_enabled(src, dst));
3117 if !base_allowed {
3118 return false;
3119 }
3120
3121 let Some(ty) = ty else {
3122 return true;
3123 };
3124 if st
3125 .typed_route_overrides
3126 .keys()
3127 .any(|(typed_src, typed_ty, _)| *typed_src == src && *typed_ty == ty.as_u32())
3128 {
3129 return st
3130 .typed_route_overrides
3131 .get(&(src, ty.as_u32(), dst))
3132 .copied()
3133 .unwrap_or(false);
3134 }
3135 true
3136 }
3137
3138 fn has_typed_route_overrides_locked(
3139 st: &RouterInner,
3140 src: Option<RouterSideId>,
3141 ty: DataType,
3142 ) -> bool {
3143 st.typed_route_overrides
3144 .keys()
3145 .any(|(typed_src, typed_ty, _)| *typed_src == src && *typed_ty == ty.as_u32())
3146 }
3147
3148 #[cfg(feature = "discovery")]
3149 fn endpoint_overlap_count<I>(reachable: I, eps: &[DataEndpoint]) -> usize
3150 where
3151 I: IntoIterator<Item = DataEndpoint>,
3152 {
3153 let mut overlap = 0usize;
3154 for ep in reachable {
3155 if eps.contains(&ep) {
3156 overlap = overlap.saturating_add(1);
3157 }
3158 }
3159 overlap
3160 }
3161
3162 #[inline]
3163 fn preferred_scoring_endpoints<'a>(
3164 &self,
3165 eps: &'a [DataEndpoint],
3166 prefer_nonlocal: bool,
3167 ) -> alloc::borrow::Cow<'a, [DataEndpoint]> {
3168 if !prefer_nonlocal || eps.iter().all(|&ep| !self.cfg.is_local_endpoint(ep)) {
3169 return alloc::borrow::Cow::Borrowed(eps);
3170 }
3171 let nonlocal: Vec<DataEndpoint> = eps
3172 .iter()
3173 .copied()
3174 .filter(|&ep| !self.cfg.is_local_endpoint(ep))
3175 .collect();
3176 if nonlocal.is_empty() {
3177 alloc::borrow::Cow::Borrowed(eps)
3178 } else {
3179 alloc::borrow::Cow::Owned(nonlocal)
3180 }
3181 }
3182
3183 fn pop_next_queued_control_rx_item(&self) -> TelemetryResult<Option<RouterRxItem>> {
3184 {
3185 let mut isr_rx = self.isr_rx_queue.try_lock()?;
3186 let idx = isr_rx.iter().position(Self::queued_rx_item_is_control);
3187 if let Some(idx) = idx {
3188 return Ok(isr_rx.remove_pos(idx));
3189 }
3190 }
3191
3192 let mut st = self.state.lock();
3193 let idx = st
3194 .received_queue
3195 .iter()
3196 .position(Self::queued_rx_item_is_control);
3197 if let Some(idx) = idx {
3198 return Ok(st.received_queue.remove_pos(idx));
3199 }
3200 Ok(None)
3201 }
3202
3203 fn queued_rx_item_is_control(item: &RouterRxItem) -> bool {
3204 if item.priority == crate::transport_priority(DataType::ManagedVariableValue) {
3207 return true;
3208 }
3209 let ty = match &item.data {
3210 RouterItem::Packet(pkt) => Some(pkt.data_type()),
3211 RouterItem::Packed(bytes) => wire_format::scheduling_type(bytes.as_ref()),
3212 };
3213 match ty {
3214 Some(
3215 DataType::ReliableAck
3216 | DataType::ReliablePartialAck
3217 | DataType::ReliablePacketRequest,
3218 ) => true,
3219 #[cfg(feature = "discovery")]
3220 Some(ty) => discovery::is_discovery_type(ty),
3221 _ => false,
3222 }
3223 }
3224
3225 fn drain_queued_control_rx_before_tx(
3226 &self,
3227 start: u64,
3228 timeout_ms: u32,
3229 ) -> TelemetryResult<bool> {
3230 let mut did_any = false;
3231 while let Some(item) = self.pop_next_queued_control_rx_item()? {
3232 self.process_rx_queue_item(item)?;
3233 did_any = true;
3234 if timeout_ms != 0 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms) {
3237 break;
3238 }
3239 }
3240 Ok(did_any)
3241 }
3242
3243 fn eligible_side_ids_locked(
3244 &self,
3245 st: &RouterInner,
3246 src: Option<RouterSideId>,
3247 ty: Option<DataType>,
3248 restrict_link_local: bool,
3249 ) -> Vec<RouterSideId> {
3250 st.sides
3251 .iter()
3252 .enumerate()
3253 .filter_map(|(side_id, side)| {
3254 let side = side.as_ref()?;
3255 if restrict_link_local && !side.opts.link_local_enabled {
3256 return None;
3257 }
3258 if self.route_allowed_locked(st, src, ty, side_id) {
3259 Some(side_id)
3260 } else {
3261 None
3262 }
3263 })
3264 .collect()
3265 }
3266
3267 fn apply_route_selection_locked(
3268 &self,
3269 st: &mut RouterInner,
3270 src: Option<RouterSideId>,
3271 mut sides: Vec<RouterSideId>,
3272 origin: RouteSelectionOrigin,
3273 ) -> Vec<RouterSideId> {
3274 if sides.len() <= 1 {
3275 return sides;
3276 }
3277
3278 let selection_mode = st.source_route_modes.get(&src).copied();
3279 if selection_mode.is_none() && origin == RouteSelectionOrigin::Discovered {
3280 return self.apply_adaptive_discovery_selection_locked(st, src, sides);
3281 }
3282
3283 match selection_mode.unwrap_or(RouteSelectionMode::Fanout) {
3284 RouteSelectionMode::Fanout => sides,
3285 RouteSelectionMode::Weighted => {
3286 crate::small_sort::sort(&mut sides);
3287 let total_weight = sides.iter().fold(0_u64, |acc, side| {
3288 acc + u64::from(st.route_weights.get(&(src, *side)).copied().unwrap_or(1))
3289 });
3290 if total_weight == 0 {
3291 return Vec::new();
3292 }
3293 let cursor = st.route_selection_cursors.entry(src).or_insert(0);
3294 let pick = *cursor % total_weight;
3295 *cursor = cursor.wrapping_add(1);
3296 let mut remaining = pick;
3297 for side in sides {
3298 let weight =
3299 u64::from(st.route_weights.get(&(src, side)).copied().unwrap_or(1));
3300 if remaining < weight {
3301 return vec![side];
3302 }
3303 remaining -= weight;
3304 }
3305 Vec::new()
3306 }
3307 RouteSelectionMode::Failover => {
3308 sides.sort_by_key(|side| {
3309 (
3310 st.route_priorities.get(&(src, *side)).copied().unwrap_or(0),
3311 *side,
3312 )
3313 });
3314 sides.truncate(1);
3315 sides
3316 }
3317 }
3318 }
3319
3320 fn apply_adaptive_discovery_selection_locked(
3321 &self,
3322 st: &mut RouterInner,
3323 src: Option<RouterSideId>,
3324 mut sides: Vec<RouterSideId>,
3325 ) -> Vec<RouterSideId> {
3326 crate::small_sort::sort(&mut sides);
3327 let mut unmeasured: Vec<_> = sides
3328 .iter()
3329 .copied()
3330 .filter(|side| !st.adaptive_route_stats.contains_key(side))
3331 .collect();
3332 if !unmeasured.is_empty() {
3333 let cursor = st.route_selection_cursors.entry(src).or_insert(0);
3334 let pick = (*cursor as usize) % unmeasured.len();
3335 *cursor = cursor.wrapping_add(1);
3336 return vec![unmeasured.swap_remove(pick)];
3337 }
3338
3339 let now_ms = self.clock.now_ms();
3340 let total_weight = sides.iter().fold(0_u64, |acc, side| {
3341 acc + st
3342 .adaptive_route_stats
3343 .get(side)
3344 .map(|stats| stats.weight(now_ms))
3345 .unwrap_or(1)
3346 });
3347 if total_weight == 0 {
3348 sides.truncate(1);
3349 return sides;
3350 }
3351
3352 let cursor = st.route_selection_cursors.entry(src).or_insert(0);
3353 let pick = *cursor % total_weight;
3354 *cursor = cursor.wrapping_add(1);
3355 let mut remaining = pick;
3356 for side in sides {
3357 let weight = st
3358 .adaptive_route_stats
3359 .get(&side)
3360 .map(|stats| stats.weight(now_ms))
3361 .unwrap_or(1);
3362 if remaining < weight {
3363 return vec![side];
3364 }
3365 remaining -= weight;
3366 }
3367 Vec::new()
3368 }
3369
3370 fn record_side_tx_sample(
3371 &self,
3372 side: RouterSideId,
3373 bytes: usize,
3374 started_ms: u64,
3375 ended_ms: u64,
3376 ) {
3377 let sample_ms = ended_ms.saturating_sub(started_ms).max(1);
3378 let sample_bps = ((bytes as u128).saturating_mul(1000) / u128::from(sample_ms))
3379 .min(u128::from(u64::MAX)) as u64;
3380 let mut st = self.state.lock();
3381 st.adaptive_route_stats
3382 .entry(side)
3383 .or_default()
3384 .observe(bytes, sample_bps, ended_ms);
3385 }
3386
3387 pub fn note_side_link_probe_sample(
3392 &self,
3393 side: RouterSideId,
3394 bytes: usize,
3395 duration_ms: u64,
3396 ) -> TelemetryResult<()> {
3397 {
3398 let st = self.state.lock();
3399 let _ = Self::side_ref(&st, side).map_err(|_| TelemetryError::BadArg)?;
3400 }
3401 let ended_ms = self.clock.now_ms();
3402 self.record_side_tx_sample(side, bytes, ended_ms.saturating_sub(duration_ms), ended_ms);
3403 Ok(())
3404 }
3405
3406 fn router_item_wire_len(data: &RouterItem) -> TelemetryResult<usize> {
3407 match data {
3408 RouterItem::Packet(pkt) => Ok(wire_format::packet_wire_size(pkt)),
3409 RouterItem::Packed(bytes) => Ok(bytes.len()),
3410 }
3411 }
3412
3413 #[inline]
3423 fn reliable_control_target_packet_id(data: &RouterItem) -> TelemetryResult<Option<u64>> {
3424 match data {
3425 RouterItem::Packet(pkt) => {
3426 if !Self::is_end_to_end_ack_packet(pkt) {
3427 return Ok(None);
3428 }
3429 Self::decode_end_to_end_reliable_ack(pkt.payload()).map(Some)
3430 }
3431 RouterItem::Packed(bytes) => {
3432 if wire_format::peek_routing_frame_info(bytes.as_ref())
3433 .ok()
3434 .is_some_and(|frame| {
3435 frame.envelope.ty != DataType::ReliableAck || frame.ack_only()
3436 })
3437 {
3438 return Ok(None);
3439 }
3440 let pkt = wire_format::unpack_packet(bytes.as_ref())?;
3441 if !Self::is_end_to_end_ack_packet(&pkt) {
3442 return Ok(None);
3443 }
3444 Self::decode_end_to_end_reliable_ack(pkt.payload()).map(Some)
3445 }
3446 }
3447 }
3448
3449 fn decode_end_to_end_reliable_ack(payload: &[u8]) -> TelemetryResult<u64> {
3450 if payload.len() != 8 {
3451 return Err(TelemetryError::Unpack("bad reliable e2e ack payload"));
3452 }
3453 Ok(u64::from_le_bytes(payload[0..8].try_into().unwrap()))
3454 }
3455
3456 #[inline]
3457 fn sender_hash(sender: &str) -> u64 {
3458 if let Some(address) = sender
3459 .strip_prefix("@addr:")
3460 .and_then(|v| v.parse::<u32>().ok())
3461 {
3462 return u64::from(address);
3463 }
3464 hash_bytes_u64(0x517C_C1B7_2722_0A95, sender.as_bytes())
3465 }
3466
3467 #[inline]
3468 fn fallback_address_for_hostname(hostname: &str) -> NodeAddress {
3469 let hash = Self::sender_hash(hostname);
3470 let mut address = (hash as u32) ^ ((hash >> 32) as u32);
3471 if address == 0 {
3472 address = 1;
3473 }
3474 address
3475 }
3476
3477 fn canonical_sender_locked(st: &RouterInner, sender: &str) -> String {
3482 let Some(address) = sender
3483 .strip_prefix("@addr:")
3484 .and_then(|value| value.parse::<u32>().ok())
3485 else {
3486 return sender.to_string();
3487 };
3488 Self::hostname_for_address_locked(st, address).unwrap_or_else(|| sender.to_string())
3489 }
3490
3491 fn hostname_for_address_locked(st: &RouterInner, address: NodeAddress) -> Option<String> {
3498 if let Some(hostname) = st.address_by_value.get(&address) {
3499 return Some(hostname.clone());
3500 }
3501
3502 let mut found: Option<String> = None;
3503 let mut collision = false;
3504 let mut consider = |candidate: &str| {
3505 if sender_address_u32(candidate) != address {
3506 return;
3507 }
3508 match found.as_deref() {
3509 None => found = Some(candidate.to_string()),
3510 Some(existing) if existing == candidate => {}
3511 Some(_) => collision = true,
3515 }
3516 };
3517
3518 for entry in st.address_book.values() {
3519 consider(entry.hostname.as_ref());
3520 }
3521 #[cfg(feature = "discovery")]
3522 for route in st.discovery_routes.values() {
3523 for (announcer, state) in &route.announcers {
3524 consider(announcer);
3525 for board in &state.topology_boards {
3526 consider(&board.sender_id);
3527 for peer in &board.connections {
3533 consider(peer);
3534 }
3535 }
3536 }
3537 }
3538 if collision { None } else { found }
3539 }
3540
3541 fn address_mode_from_code(mode: u8, requested: NodeAddress) -> AddressAssignmentMode {
3542 match mode {
3543 2 => AddressAssignmentMode::Static(if requested == 0 { 1 } else { requested }),
3544 1 => AddressAssignmentMode::Requested(if requested == 0 { 1 } else { requested }),
3545 _ => AddressAssignmentMode::Dynamic,
3546 }
3547 }
3548
3549 fn address_winner_pref(entry: &AddressBookEntry) -> (u8, core::cmp::Reverse<u64>, u64) {
3550 let rank = match entry.mode {
3551 AddressAssignmentMode::Static(_) => 2,
3552 AddressAssignmentMode::Requested(_) => 1,
3553 AddressAssignmentMode::Dynamic => 0,
3554 };
3555 (
3556 rank,
3557 core::cmp::Reverse(entry.birth_ms),
3558 u64::MAX - entry.owner_hash,
3559 )
3560 }
3561
3562 fn identity_winner_pref(entry: &AddressBookEntry) -> (core::cmp::Reverse<u64>, u64) {
3563 (
3564 core::cmp::Reverse(entry.birth_ms),
3565 u64::MAX - entry.owner_hash,
3566 )
3567 }
3568
3569 fn allocate_free_address_locked(st: &RouterInner, seed: NodeAddress) -> NodeAddress {
3570 let mut candidate = if seed == 0 { 1 } else { seed };
3571 for _ in 0..u32::MAX {
3572 if candidate != 0 && !st.address_by_value.contains_key(&candidate) {
3573 return candidate;
3574 }
3575 candidate = candidate.wrapping_add(1);
3576 if candidate == 0 {
3577 candidate = 1;
3578 }
3579 }
3580 1
3581 }
3582
3583 fn unique_hostname_locked(st: &RouterInner, base: &str, owner_hash: u64) -> String {
3584 if !st.address_book.contains_key(base) {
3585 return base.to_string();
3586 }
3587 let stem = if base.is_empty() { "node" } else { base };
3588 let mut candidate = format!("{stem}-{owner_hash:08x}");
3589 let mut suffix = 1u32;
3590 while st.address_book.contains_key(&candidate) {
3591 candidate = format!("{stem}-{owner_hash:08x}-{suffix}");
3592 suffix = suffix.saturating_add(1);
3593 }
3594 candidate
3595 }
3596
3597 fn update_local_identity_locked(
3598 &self,
3599 st: &mut RouterInner,
3600 mut local: AddressBookEntry,
3601 reason: AddressChangeReason,
3602 ) -> AddressChange {
3603 let old = st.local_address.clone();
3604 st.address_book.remove(old.hostname.as_ref());
3605 st.address_by_value.remove(&old.address);
3606 local.last_seen_ms = self.clock.now_ms();
3607 st.address_by_value
3608 .insert(local.address, local.hostname.to_string());
3609 st.address_book
3610 .insert(local.hostname.to_string(), local.clone());
3611 st.local_address = local.clone();
3612 *self.sender.lock() = local.hostname.clone();
3613 #[cfg(feature = "discovery")]
3614 Self::note_discovery_topology_change_locked(st, self.clock.now_ms());
3615 AddressChange {
3616 old_hostname: old.hostname,
3617 new_hostname: local.hostname,
3618 old_address: old.address,
3619 new_address: local.address,
3620 reason,
3621 }
3622 }
3623
3624 fn notify_address_change(&self, change: AddressChange) -> TelemetryResult<()> {
3625 for handler in self.cfg.address_change_handlers.iter() {
3626 (handler.handler)(change.clone())?;
3627 }
3628 Ok(())
3629 }
3630
3631 #[cfg(feature = "discovery")]
3632 fn local_address_advertisement(
3633 &self,
3634 reachable_endpoints: Vec<DataEndpoint>,
3635 reachable_network_variables: Vec<DataType>,
3636 reachable_timesync_sources: Vec<String>,
3637 link_capabilities: discovery::LinkCapabilities,
3638 state: u8,
3639 ) -> discovery::AddressAdvertisement {
3640 let st = self.state.lock();
3641 discovery::AddressAdvertisement {
3642 hostname: st.local_address.hostname.to_string(),
3643 address: st.local_address.address,
3644 requested_address: st.local_address.requested_address,
3645 mode: st.local_address.mode.mode_code(),
3646 state,
3647 birth_ms: st.local_address.birth_ms,
3648 owner_hash: st.local_address.owner_hash,
3649 reachable_endpoints,
3650 reachable_network_variables,
3651 reachable_timesync_sources,
3652 link_capabilities,
3653 }
3654 }
3655
3656 #[cfg(feature = "discovery")]
3657 fn ingest_address_advertisement(
3658 &self,
3659 ad: discovery::AddressAdvertisement,
3660 ) -> TelemetryResult<bool> {
3661 let now_ms = self.clock.now_ms();
3662 let mut remote = AddressBookEntry {
3663 hostname: Arc::from(ad.hostname.as_str()),
3664 address: ad.address,
3665 requested_address: ad.requested_address,
3666 mode: Self::address_mode_from_code(ad.mode, ad.requested_address),
3667 birth_ms: ad.birth_ms,
3668 owner_hash: ad.owner_hash,
3669 last_seen_ms: now_ms,
3670 };
3671 let mut change = None;
3672 let mut changed = false;
3673 {
3674 let mut st = self.state.lock();
3675 let local = st.local_address.clone();
3676 let hostname_conflict =
3677 remote.hostname == local.hostname && remote.owner_hash != local.owner_hash;
3678 let address_conflict =
3679 remote.address == local.address && remote.owner_hash != local.owner_hash;
3680
3681 if hostname_conflict
3682 && Self::identity_winner_pref(&remote) > Self::identity_winner_pref(&local)
3683 {
3684 let mut next = local.clone();
3685 next.hostname = Arc::from(Self::unique_hostname_locked(
3686 &st,
3687 local.hostname.as_ref(),
3688 local.owner_hash,
3689 ));
3690 change = Some(self.update_local_identity_locked(
3691 &mut st,
3692 next,
3693 AddressChangeReason::HostnameConflict,
3694 ));
3695 changed = true;
3696 } else if address_conflict
3697 && Self::address_winner_pref(&remote) > Self::address_winner_pref(&local)
3698 {
3699 let mut next = local.clone();
3700 let seed = next
3701 .requested_address
3702 .max(Self::fallback_address_for_hostname(next.hostname.as_ref()));
3703 next.address = Self::allocate_free_address_locked(&st, seed);
3704 let reason = match next.mode {
3705 AddressAssignmentMode::Static(_) => AddressChangeReason::StaticConflict,
3706 AddressAssignmentMode::Requested(_) => AddressChangeReason::RequestedConflict,
3707 AddressAssignmentMode::Dynamic => AddressChangeReason::DynamicConflict,
3708 };
3709 change = Some(self.update_local_identity_locked(&mut st, next, reason));
3710 changed = true;
3711 } else if hostname_conflict {
3712 remote.hostname = Arc::from(Self::unique_hostname_locked(
3713 &st,
3714 remote.hostname.as_ref(),
3715 remote.owner_hash,
3716 ));
3717 changed = true;
3718 } else if address_conflict {
3719 let seed = remote
3720 .requested_address
3721 .max(Self::fallback_address_for_hostname(
3722 remote.hostname.as_ref(),
3723 ));
3724 remote.address = Self::allocate_free_address_locked(&st, seed);
3725 changed = true;
3726 }
3727
3728 st.address_book.remove(remote.hostname.as_ref());
3729 st.address_by_value.remove(&remote.address);
3730 st.address_by_value
3731 .insert(remote.address, remote.hostname.to_string());
3732 st.address_book
3733 .insert(remote.hostname.to_string(), remote.clone());
3734 }
3735 if let Some(change) = change {
3736 self.notify_address_change(change)?;
3737 }
3738 Ok(changed)
3739 }
3740
3741 #[inline]
3742 fn is_end_to_end_ack_sender(sender: &str) -> bool {
3743 sender == Self::END_TO_END_ACK_SENDER || sender.starts_with(Self::END_TO_END_ACK_PREFIX)
3744 }
3745
3746 fn decode_end_to_end_ack_sender_hash(sender: &str) -> Option<u64> {
3747 if let Some(ack_sender) = sender.strip_prefix(Self::END_TO_END_ACK_PREFIX)
3748 && !ack_sender.is_empty()
3749 {
3750 return Some(Self::sender_hash(ack_sender));
3751 }
3752 None
3753 }
3754
3755 fn end_to_end_ack_sender_hash(pkt: &Packet) -> Option<u64> {
3761 if pkt.data_type() != DataType::ReliableAck {
3762 return None;
3763 }
3764 pkt.wire_target_senders()
3765 .first()
3766 .copied()
3767 .or_else(|| Self::decode_end_to_end_ack_sender_hash(pkt.sender()))
3768 }
3769
3770 fn is_end_to_end_ack_packet(pkt: &Packet) -> bool {
3771 Self::end_to_end_ack_sender_hash(pkt).is_some()
3772 && Self::decode_end_to_end_reliable_ack(pkt.payload()).is_ok()
3773 }
3774
3775 fn encode_end_to_end_ack_sender(&self) -> String {
3776 let sender = self.sender_arc();
3777 format!("{}{}", Self::END_TO_END_ACK_PREFIX, sender)
3778 }
3779
3780 #[cfg(feature = "discovery")]
3781 fn is_end_to_end_destination_sender(sender: &str) -> bool {
3782 sender != "RELAY" && !Self::is_end_to_end_ack_sender(sender)
3783 }
3784
3785 fn encode_end_to_end_reliable_ack(packet_id: u64) -> crate::SharedBytes {
3786 let mut payload = Vec::with_capacity(8);
3787 payload.extend_from_slice(&packet_id.to_le_bytes());
3788 crate::shared_bytes::convert(Arc::<[u8]>::from(payload))
3789 }
3790
3791 fn encode_p2p_payload(
3792 src_hostname: &str,
3793 src_address: NodeAddress,
3794 src_port: P2pPort,
3795 dst_port: P2pPort,
3796 payload: &[u8],
3797 ) -> TelemetryResult<crate::SharedBytes> {
3798 let host_len = u16::try_from(src_hostname.len())
3799 .map_err(|_| TelemetryError::Pack("p2p hostname too long"))?;
3800 let payload_len = u32::try_from(payload.len())
3801 .map_err(|_| TelemetryError::Pack("p2p payload too long"))?;
3802 let mut out = Vec::with_capacity(
3803 15usize
3804 .saturating_add(src_hostname.len())
3805 .saturating_add(payload.len()),
3806 );
3807 out.push(1);
3808 out.extend_from_slice(&dst_port.to_le_bytes());
3809 out.extend_from_slice(&src_port.to_le_bytes());
3810 out.extend_from_slice(&src_address.to_le_bytes());
3811 out.extend_from_slice(&host_len.to_le_bytes());
3812 out.extend_from_slice(&payload_len.to_le_bytes());
3813 out.extend_from_slice(src_hostname.as_bytes());
3814 out.extend_from_slice(payload);
3815 Ok(out.into())
3816 }
3817
3818 fn decode_p2p_payload(payload: &[u8]) -> TelemetryResult<P2pDecoded<'_>> {
3819 if payload.len() < 15 {
3820 return Err(TelemetryError::Unpack("p2p frame short"));
3821 }
3822 if payload[0] != 1 {
3823 return Err(TelemetryError::Unpack("p2p frame version"));
3824 }
3825 let destination_port =
3826 u16::from_le_bytes(payload[1..3].try_into().expect("2-byte dst port"));
3827 let source_port = u16::from_le_bytes(payload[3..5].try_into().expect("2-byte src port"));
3828 let source_address = u32::from_le_bytes(payload[5..9].try_into().expect("4-byte address"));
3829 let host_len =
3830 u16::from_le_bytes(payload[9..11].try_into().expect("2-byte host len")) as usize;
3831 let body_len =
3832 u32::from_le_bytes(payload[11..15].try_into().expect("4-byte body len")) as usize;
3833 let host_start = 15usize;
3834 let host_end = host_start.saturating_add(host_len);
3835 let body_end = host_end.saturating_add(body_len);
3836 if host_end > payload.len() || body_end != payload.len() {
3837 return Err(TelemetryError::Unpack("p2p frame length"));
3838 }
3839 let source_hostname = core::str::from_utf8(&payload[host_start..host_end])
3840 .map_err(|_| TelemetryError::Unpack("p2p hostname utf8"))?;
3841 Ok(P2pDecoded {
3842 source_hostname,
3843 source_address,
3844 source_port,
3845 destination_port,
3846 payload: &payload[host_end..body_end],
3847 })
3848 }
3849
3850 fn encode_p2p_stream_payload(
3851 flags: u8,
3852 source_stream_id: P2pStreamId,
3853 destination_stream_id: P2pStreamId,
3854 sequence: u32,
3855 payload: &[u8],
3856 ) -> TelemetryResult<crate::SharedBytes> {
3857 let payload_len = u32::try_from(payload.len())
3858 .map_err(|_| TelemetryError::Pack("p2p stream payload too long"))?;
3859 let mut out = Vec::with_capacity(22usize.saturating_add(payload.len()));
3860 out.extend_from_slice(&P2P_STREAM_MAGIC);
3861 out.push(P2P_STREAM_VERSION);
3862 out.push(flags);
3863 out.extend_from_slice(&source_stream_id.to_le_bytes());
3864 out.extend_from_slice(&destination_stream_id.to_le_bytes());
3865 out.extend_from_slice(&sequence.to_le_bytes());
3866 out.extend_from_slice(&payload_len.to_le_bytes());
3867 out.extend_from_slice(payload);
3868 Ok(out.into())
3869 }
3870
3871 fn decode_p2p_stream_payload(payload: &[u8]) -> TelemetryResult<Option<P2pStreamDecoded<'_>>> {
3872 if !payload.starts_with(&P2P_STREAM_MAGIC) {
3873 return Ok(None);
3874 }
3875 if payload.len() < 22 {
3876 return Err(TelemetryError::Unpack("p2p stream frame short"));
3877 }
3878 if payload[4] != P2P_STREAM_VERSION {
3879 return Err(TelemetryError::Unpack("p2p stream frame version"));
3880 }
3881 let flags = payload[5];
3882 let source_stream_id =
3883 u32::from_le_bytes(payload[6..10].try_into().expect("stream source id"));
3884 let destination_stream_id =
3885 u32::from_le_bytes(payload[10..14].try_into().expect("stream destination id"));
3886 let sequence = u32::from_le_bytes(payload[14..18].try_into().expect("stream sequence"));
3887 let body_len =
3888 u32::from_le_bytes(payload[18..22].try_into().expect("stream body len")) as usize;
3889 let body_end = 22usize.saturating_add(body_len);
3890 if body_end != payload.len() {
3891 return Err(TelemetryError::Unpack("p2p stream frame length"));
3892 }
3893 Ok(Some(P2pStreamDecoded {
3894 flags,
3895 source_stream_id,
3896 destination_stream_id,
3897 sequence,
3898 payload: &payload[22..],
3899 }))
3900 }
3901
3902 fn allocate_p2p_stream_id_locked(st: &mut RouterInner) -> P2pStreamId {
3903 for _ in 0..u32::MAX {
3904 let id = st.next_p2p_stream_id.max(1);
3905 st.next_p2p_stream_id = st.next_p2p_stream_id.wrapping_add(1).max(1);
3906 if !st.p2p_stream_sessions.contains_key(&id) {
3907 return id;
3908 }
3909 }
3910 0
3911 }
3912
3913 fn dispatch_p2p_packet(&self, pkt: &Packet) -> TelemetryResult<()> {
3914 if pkt.data_type() != DataType::P2pMessage {
3915 return Ok(());
3916 }
3917 let decoded = Self::decode_p2p_payload(pkt.payload())?;
3918 if let Some(stream) = Self::decode_p2p_stream_payload(decoded.payload)? {
3919 return self.dispatch_p2p_stream_frame(&decoded, &stream);
3920 }
3921 let handlers = {
3922 let st = self.state.lock();
3923 st.p2p_port_handlers
3924 .get(&decoded.destination_port)
3925 .cloned()
3926 .unwrap_or_default()
3927 };
3928 for handler in handlers {
3929 (handler.handler)(P2pMessage {
3930 source_hostname: decoded.source_hostname,
3931 source_address: decoded.source_address,
3932 source_port: decoded.source_port,
3933 destination_port: decoded.destination_port,
3934 payload: decoded.payload,
3935 })?;
3936 }
3937 Ok(())
3938 }
3939
3940 fn dispatch_p2p_stream_frame(
3941 &self,
3942 msg: &P2pDecoded<'_>,
3943 frame: &P2pStreamDecoded<'_>,
3944 ) -> TelemetryResult<()> {
3945 let mut events: Vec<PendingP2pStreamEvent> = Vec::new();
3946 let mut reply: Option<(AddressBookEntry, P2pPort, P2pPort, crate::SharedBytes)> = None;
3947 {
3948 let mut st = self.state.lock();
3949 if frame.flags & P2P_STREAM_SYN != 0 && frame.flags & P2P_STREAM_ACK == 0 {
3950 let peer_hostname: Arc<str> = Arc::from(msg.source_hostname);
3951 let existing_id = st.p2p_stream_sessions.iter().find_map(|(id, session)| {
3952 (session.peer_stream_id == frame.source_stream_id
3953 && session.local_port == msg.destination_port
3954 && session.peer_port == msg.source_port
3955 && session.peer_address == msg.source_address
3956 && session.peer_hostname.as_ref() == msg.source_hostname)
3957 .then_some(*id)
3958 });
3959 let local_id = if let Some(local_id) = existing_id {
3960 local_id
3961 } else {
3962 let local_id = Self::allocate_p2p_stream_id_locked(&mut st);
3963 if local_id == 0 {
3964 return Err(TelemetryError::Io("p2p stream id exhausted"));
3965 }
3966 st.p2p_stream_sessions.insert(
3967 local_id,
3968 P2pStreamSession {
3969 peer_hostname: peer_hostname.clone(),
3970 peer_address: msg.source_address,
3971 local_port: msg.destination_port,
3972 peer_port: msg.source_port,
3973 peer_stream_id: frame.source_stream_id,
3974 next_sequence: 1,
3975 connected: true,
3976 },
3977 );
3978 let handlers = st
3979 .p2p_stream_handlers
3980 .get(&msg.destination_port)
3981 .cloned()
3982 .unwrap_or_default();
3983 events.push(PendingP2pStreamEvent {
3984 handlers,
3985 kind: P2pStreamEventKind::Accepted,
3986 stream_id: local_id,
3987 peer_stream_id: frame.source_stream_id,
3988 peer_hostname: peer_hostname.clone(),
3989 peer_address: msg.source_address,
3990 local_port: msg.destination_port,
3991 peer_port: msg.source_port,
3992 });
3993 local_id
3994 };
3995 let dst =
3996 st.address_book
3997 .get(msg.source_hostname)
3998 .cloned()
3999 .unwrap_or(AddressBookEntry {
4000 hostname: peer_hostname,
4001 address: msg.source_address,
4002 requested_address: msg.source_address,
4003 mode: AddressAssignmentMode::Dynamic,
4004 birth_ms: self.clock.now_ms(),
4005 owner_hash: Self::sender_hash(msg.source_hostname),
4006 last_seen_ms: self.clock.now_ms(),
4007 });
4008 let payload = Self::encode_p2p_stream_payload(
4009 P2P_STREAM_SYN | P2P_STREAM_ACK,
4010 local_id,
4011 frame.source_stream_id,
4012 0,
4013 &[],
4014 )?;
4015 reply = Some((dst, msg.source_port, msg.destination_port, payload));
4016 } else if frame.flags & P2P_STREAM_SYN != 0 && frame.flags & P2P_STREAM_ACK != 0 {
4017 if let Some(session) = st.p2p_stream_sessions.get_mut(&frame.destination_stream_id)
4018 {
4019 session.peer_stream_id = frame.source_stream_id;
4020 session.connected = true;
4021 let peer_hostname = session.peer_hostname.clone();
4022 let peer_address = session.peer_address;
4023 let local_port = session.local_port;
4024 let peer_port = session.peer_port;
4025 let handlers = st
4026 .p2p_stream_handlers
4027 .get(&local_port)
4028 .cloned()
4029 .unwrap_or_default();
4030 events.push(PendingP2pStreamEvent {
4031 handlers,
4032 kind: P2pStreamEventKind::Connected,
4033 stream_id: frame.destination_stream_id,
4034 peer_stream_id: frame.source_stream_id,
4035 peer_hostname,
4036 peer_address,
4037 local_port,
4038 peer_port,
4039 });
4040 }
4041 } else if frame.flags & (P2P_STREAM_FIN | P2P_STREAM_RST | P2P_STREAM_DATA) != 0 {
4042 let kind = if frame.flags & P2P_STREAM_RST != 0 {
4043 P2pStreamEventKind::Reset
4044 } else if frame.flags & P2P_STREAM_FIN != 0 {
4045 P2pStreamEventKind::Closed
4046 } else {
4047 P2pStreamEventKind::Data
4048 };
4049 let session_id = if frame.destination_stream_id != 0 {
4050 Some(frame.destination_stream_id)
4051 } else {
4052 st.p2p_stream_sessions.iter().find_map(|(id, session)| {
4053 (session.peer_stream_id == frame.source_stream_id
4054 && session.local_port == msg.destination_port
4055 && session.peer_port == msg.source_port
4056 && session.peer_address == msg.source_address
4057 && session.peer_hostname.as_ref() == msg.source_hostname)
4058 .then_some(*id)
4059 })
4060 };
4061 if let Some(session_id) = session_id
4062 && let Some(session) = st.p2p_stream_sessions.get(&session_id)
4063 {
4064 let handlers = st
4065 .p2p_stream_handlers
4066 .get(&session.local_port)
4067 .cloned()
4068 .unwrap_or_default();
4069 events.push(PendingP2pStreamEvent {
4070 handlers,
4071 kind,
4072 stream_id: session_id,
4073 peer_stream_id: frame.source_stream_id,
4074 peer_hostname: session.peer_hostname.clone(),
4075 peer_address: session.peer_address,
4076 local_port: session.local_port,
4077 peer_port: session.peer_port,
4078 });
4079 if matches!(kind, P2pStreamEventKind::Closed | P2pStreamEventKind::Reset) {
4080 st.p2p_stream_sessions.remove(&session_id);
4081 }
4082 }
4083 }
4084 }
4085 if let Some((dst, dst_port, src_port, payload)) = reply {
4086 self.send_p2p_to_entry(dst, dst_port, src_port, &payload)?;
4087 }
4088 for pending in events {
4089 for handler in pending.handlers {
4090 (handler.handler)(P2pStreamEvent {
4091 kind: pending.kind,
4092 stream_id: pending.stream_id,
4093 peer_stream_id: pending.peer_stream_id,
4094 sequence: frame.sequence,
4095 peer_hostname: pending.peer_hostname.as_ref(),
4096 peer_address: pending.peer_address,
4097 local_port: pending.local_port,
4098 peer_port: pending.peer_port,
4099 payload: frame.payload,
4100 })?;
4101 }
4102 }
4103 Ok(())
4104 }
4105
4106 fn note_reliable_return_route(&self, side: RouterSideId, packet_id: u64) -> bool {
4112 let mut st = self.state.lock();
4113 if st.end_to_end_reliable_tx.contains_key(&packet_id)
4117 && st
4118 .reliable_return_routes
4119 .get(&packet_id)
4120 .is_some_and(|route| route.upstream_contract.is_some())
4121 {
4122 return false;
4123 }
4124 let now = self.clock.now_ms();
4128 let retry_ms = runtime_reliable_retransmit_ms();
4129 let duplicate = st.recent_rx.contains(&packet_id);
4130 let last_replay_ms = if duplicate {
4131 now.saturating_sub(retry_ms)
4132 } else {
4133 now
4134 };
4135 let replace_side = !duplicate
4141 || st
4142 .reliable_return_routes
4143 .get(&packet_id)
4144 .is_none_or(|route| {
4145 now.saturating_sub(route.last_replay_ms) >= retry_ms
4146 || !Self::side_ref(&st, route.side)
4147 .is_ok_and(|side| side.opts.egress_enabled)
4148 });
4149 if !Self::remember_reliable_return_route_locked(&mut st, packet_id) {
4150 return true;
4151 }
4152 st.reliable_return_routes
4153 .entry(packet_id)
4154 .and_modify(|route| {
4155 if replace_side {
4156 route.side = side;
4157 }
4158 })
4159 .or_insert(ReliableReturnRouteState {
4160 side,
4161 last_replay_ms,
4162 upstream_contract: None,
4163 });
4164 true
4165 }
4166
4167 fn remember_reliable_return_route_locked(st: &mut RouterInner, packet_id: u64) -> bool {
4173 let cap = runtime_reliable_max_return_routes().max(1);
4174 st.reliable_return_route_order
4175 .retain(|id| st.reliable_return_routes.contains_key(id) && *id != packet_id);
4176 while st.reliable_return_route_order.len() >= cap {
4177 let Some(index) = st
4178 .reliable_return_route_order
4179 .iter()
4180 .position(|id| !st.end_to_end_reliable_tx.contains_key(id))
4181 else {
4182 return false;
4183 };
4184 if let Some(oldest) = st.reliable_return_route_order.remove(index) {
4185 st.reliable_return_routes.remove(&oldest);
4186 }
4187 }
4188 st.reliable_return_route_order.push_back(packet_id);
4189 true
4190 }
4191
4192 fn remember_end_to_end_reliable_tx_locked(st: &mut RouterInner, packet_id: u64) {
4193 let cap = runtime_reliable_max_end_to_end_pending().max(1);
4194 st.end_to_end_reliable_tx_order
4195 .retain(|id| st.end_to_end_reliable_tx.contains_key(id) && *id != packet_id);
4196 while st.end_to_end_reliable_tx_order.len() >= cap {
4197 if let Some(oldest) = st.end_to_end_reliable_tx_order.pop_front() {
4198 st.end_to_end_reliable_tx.remove(&oldest);
4199 } else {
4200 break;
4201 }
4202 }
4203 st.end_to_end_reliable_tx_order.push_back(packet_id);
4204 }
4205
4206 #[cfg(feature = "discovery")]
4207 fn expected_end_to_end_destinations_locked(
4208 &self,
4209 st: &RouterInner,
4210 data: &RouterItem,
4211 exclude: Option<RouterSideId>,
4212 managed: bool,
4213 ) -> TelemetryResult<BTreeMap<u64, RouterSideId>> {
4214 let (eps, ty) = self.item_route_info(data)?;
4215 let now_ms = self.clock.now_ms();
4216 let restrict_link_local = Self::endpoints_are_link_local_only(&eps);
4217 if managed || st.managed_variable_types.contains(&ty.as_u32()) {
4226 let mut out = BTreeMap::new();
4227 for (&side, route) in st.discovery_routes.iter() {
4228 if Some(side) == exclude {
4229 continue;
4230 }
4231 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4232 continue;
4233 }
4234 let Some(side_ref) = st.sides.get(side).and_then(Option::as_ref) else {
4235 continue;
4236 };
4237 if restrict_link_local && !side_ref.opts.link_local_enabled {
4238 continue;
4239 }
4240 if !self.route_allowed_locked(st, exclude, Some(ty), side) {
4241 continue;
4242 }
4243 for (sender_id, sender_state) in route.announcers.iter() {
4244 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
4245 || !sender_state.reachable_network_variables.contains(&ty)
4246 || !Self::is_end_to_end_destination_sender(sender_id)
4247 {
4248 continue;
4249 }
4250 out.insert(Self::sender_hash(sender_id), side);
4251 if out.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4252 return Ok(out);
4253 }
4254 }
4255 }
4256 return Ok(out);
4257 }
4258 let prefer_best_overlap =
4259 is_reliable_type(ty) && Self::reliable_control_target_packet_id(data)?.is_none();
4260 let scoring_eps = self.preferred_scoring_endpoints(&eps, prefer_best_overlap);
4261 let mut best_overlap = 0usize;
4262 let limit = runtime_reliable_max_end_to_end_pending().max(1);
4263 let mut out = BTreeMap::new();
4264 for (&side, route) in st.discovery_routes.iter() {
4265 if Some(side) == exclude {
4266 continue;
4267 }
4268 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4269 continue;
4270 }
4271 let Some(side_ref) = st.sides.get(side).and_then(Option::as_ref) else {
4272 continue;
4273 };
4274 if restrict_link_local && !side_ref.opts.link_local_enabled {
4275 continue;
4276 }
4277 if !self.route_allowed_locked(st, exclude, Some(ty), side) {
4278 continue;
4279 }
4280 for sender_state in route.announcers.values() {
4281 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4282 continue;
4283 }
4284 for board in sender_state.topology_boards.iter() {
4285 if !Self::is_end_to_end_destination_sender(&board.sender_id) {
4286 continue;
4287 }
4288 let overlap = Self::endpoint_overlap_count(
4289 board.reachable_endpoints.iter().copied(),
4290 &scoring_eps,
4291 );
4292 if overlap > 0 {
4293 if prefer_best_overlap {
4294 if overlap < best_overlap {
4295 continue;
4296 }
4297 if overlap > best_overlap {
4298 out.clear();
4299 best_overlap = overlap;
4300 }
4301 if out.len() >= limit {
4305 continue;
4306 }
4307 }
4308 out.insert(Self::sender_hash(&board.sender_id), side);
4309 if !prefer_best_overlap && out.len() >= limit {
4310 return Ok(out);
4311 }
4312 }
4313 }
4314 }
4315 }
4316
4317 Ok(out)
4318 }
4319
4320 #[cfg(feature = "discovery")]
4321 #[allow(clippy::too_many_arguments)]
4322 fn discovered_route_candidates_locked(
4323 &self,
4324 st: &RouterInner,
4325 exclude: Option<RouterSideId>,
4326 ty: DataType,
4327 eps: &[DataEndpoint],
4328 target_senders: &[u64],
4329 prefer_nonlocal: bool,
4330 preferred_timesync_source: Option<&str>,
4331 ) -> Vec<DiscoveryCandidateMatch> {
4332 let restrict_link_local = Self::endpoints_are_link_local_only(eps);
4333 let now_ms = self.clock.now_ms();
4334 let scoring_eps = self.preferred_scoring_endpoints(eps, prefer_nonlocal);
4335 let has_variable_owner = st.discovery_routes.iter().any(|(&side, route)| {
4336 exclude != Some(side)
4337 && now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
4338 && route.reachable_network_variables.contains(&ty)
4339 });
4340 let mut out = Vec::new();
4341 let mut unique_endpoint_fallbacks = Vec::new();
4342
4343 for (&side, route) in st.discovery_routes.iter() {
4344 if exclude == Some(side)
4345 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
4346 {
4347 continue;
4348 }
4349 if restrict_link_local
4350 && st
4351 .sides
4352 .get(side)
4353 .and_then(|s| s.as_ref())
4354 .map(|s| !s.opts.link_local_enabled)
4355 .unwrap_or(true)
4356 {
4357 continue;
4358 }
4359 if !self.route_allowed_locked(st, exclude, Some(ty), side) {
4360 continue;
4361 }
4362 if !target_senders.is_empty()
4363 && !Self::side_matches_target_senders_locked(st, side, target_senders, now_ms)
4364 {
4365 if scoring_eps
4366 .iter()
4367 .copied()
4368 .any(|ep| route.reachable.contains(&ep))
4369 {
4370 unique_endpoint_fallbacks.push(side);
4371 }
4372 continue;
4373 }
4374 if !target_senders.is_empty() {
4375 out.push(DiscoveryCandidateMatch {
4376 side,
4377 overlap: usize::MAX,
4378 });
4379 continue;
4380 }
4381 if has_variable_owner {
4382 if route.reachable_network_variables.contains(&ty) {
4383 out.push(DiscoveryCandidateMatch {
4384 side,
4385 overlap: usize::MAX,
4386 });
4387 }
4388 continue;
4389 }
4390 if preferred_timesync_source
4391 .is_some_and(|source| route.reachable_timesync_sources.iter().any(|s| s == source))
4392 {
4393 out.push(DiscoveryCandidateMatch {
4394 side,
4395 overlap: usize::MAX,
4396 });
4397 continue;
4398 }
4399 let overlap =
4400 Self::endpoint_overlap_count(route.reachable.iter().copied(), &scoring_eps);
4401 if overlap > 0 {
4402 out.push(DiscoveryCandidateMatch { side, overlap });
4403 }
4404 }
4405 if !target_senders.is_empty() && out.is_empty() && unique_endpoint_fallbacks.len() == 1 {
4406 out.push(DiscoveryCandidateMatch {
4407 side: unique_endpoint_fallbacks[0],
4408 overlap: usize::MAX,
4409 });
4410 }
4411 out
4412 }
4413
4414 #[cfg(feature = "discovery")]
4420 fn retain_shortest_discovery_candidates_locked(
4421 st: &RouterInner,
4422 matches: &mut Vec<DiscoveryCandidateMatch>,
4423 endpoints: &[DataEndpoint],
4424 target_senders: &[u64],
4425 now_ms: u64,
4426 ) {
4427 if matches.len() <= 1 || endpoints.is_empty() {
4428 return;
4429 }
4430
4431 let route_distance = |side: RouterSideId, target_sender: Option<u64>| -> Option<usize> {
4432 let route = st.discovery_routes.get(&side)?;
4433 route
4434 .announcers
4435 .iter()
4436 .filter(|(_, state)| {
4437 now_ms.saturating_sub(state.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
4438 })
4439 .filter_map(|(announcer, state)| {
4440 let targets: BTreeSet<&str> = state
4441 .topology_boards
4442 .iter()
4443 .filter(|board| {
4444 target_sender
4445 .map(|target| Self::sender_hash(&board.sender_id) == target)
4446 .unwrap_or_else(|| {
4447 endpoints.iter().any(|endpoint| {
4448 board.reachable_endpoints.contains(endpoint)
4449 })
4450 })
4451 })
4452 .map(|board| board.sender_id.as_str())
4453 .collect();
4454 if targets.is_empty() {
4455 return if let Some(target) = target_sender {
4456 state
4457 .topology_boards
4458 .iter()
4459 .any(|board| Self::sender_hash(&board.sender_id) == target)
4460 .then_some(usize::MAX / 2)
4461 } else {
4462 state
4463 .reachable
4464 .iter()
4465 .any(|endpoint| endpoints.contains(endpoint))
4466 .then_some(usize::MAX / 2)
4467 };
4468 }
4469
4470 let mut distances: BTreeMap<&str, usize> = BTreeMap::new();
4471 let mut pending = VecDeque::from([(announcer.as_str(), 0usize)]);
4472 while let Some((sender, distance)) = pending.pop_front() {
4473 if distances.contains_key(sender) {
4474 continue;
4475 }
4476 distances.insert(sender, distance);
4477 if targets.contains(sender) {
4478 return Some(distance);
4479 }
4480 for board in &state.topology_boards {
4481 if board.sender_id == sender {
4482 for peer in &board.connections {
4483 if !distances.contains_key(peer.as_str()) {
4484 pending.push_back((peer.as_str(), distance + 1));
4485 }
4486 }
4487 } else if board.connections.iter().any(|peer| peer == sender)
4488 && !distances.contains_key(board.sender_id.as_str())
4489 {
4490 pending.push_back((board.sender_id.as_str(), distance + 1));
4491 }
4492 }
4493 }
4494 None
4495 })
4496 .min()
4497 };
4498
4499 let route_targets: Vec<Option<u64>> = if target_senders.is_empty() {
4500 vec![None]
4501 } else {
4502 target_senders.iter().copied().map(Some).collect()
4503 };
4504 let mut selected = BTreeSet::new();
4505 for target in route_targets {
4506 let distances: Vec<(RouterSideId, usize)> = matches
4507 .iter()
4508 .filter_map(|candidate| {
4509 route_distance(candidate.side, target)
4510 .map(|distance| (candidate.side, distance))
4511 })
4512 .collect();
4513 let Some(best) = distances.iter().map(|(_, distance)| *distance).min() else {
4514 continue;
4515 };
4516 selected.extend(
4517 distances
4518 .into_iter()
4519 .filter(|(_, distance)| *distance == best)
4520 .map(|(side, _)| side),
4521 );
4522 }
4523 if !selected.is_empty() {
4524 matches.retain(|candidate| selected.contains(&candidate.side));
4525 }
4526 }
4527
4528 #[cfg(feature = "discovery")]
4529 fn select_discovered_candidate_sides_locked(
4530 &self,
4531 st: &mut RouterInner,
4532 exclude: Option<RouterSideId>,
4533 ty: DataType,
4534 target_senders: &[u64],
4535 prefer_best_overlap: bool,
4536 matches: Vec<DiscoveryCandidateMatch>,
4537 ) -> Vec<RouterSideId> {
4538 let discovered_origin = if Self::has_typed_route_overrides_locked(st, exclude, ty)
4539 || !target_senders.is_empty()
4540 {
4541 RouteSelectionOrigin::Flood
4542 } else {
4543 RouteSelectionOrigin::Discovered
4544 };
4545
4546 let mut matches = matches;
4547 if matches.iter().any(|m| m.overlap == usize::MAX) {
4548 matches.retain(|m| m.overlap == usize::MAX);
4549 }
4550
4551 let selected: Vec<RouterSideId> = if prefer_best_overlap {
4552 let best_overlap = matches.iter().map(|m| m.overlap).max().unwrap_or(0);
4553 matches
4554 .into_iter()
4555 .filter(|m| m.overlap == best_overlap)
4556 .map(|m| m.side)
4557 .collect()
4558 } else {
4559 matches.into_iter().map(|m| m.side).collect()
4560 };
4561
4562 let has_distributed_variable_owners = selected.iter().any(|side| {
4569 st.discovery_routes
4570 .get(side)
4571 .is_some_and(|route| route.reachable_network_variables.contains(&ty))
4572 });
4573 if DataType::try_named("HEARTBEAT") == Some(ty) || has_distributed_variable_owners {
4574 selected
4575 } else {
4576 self.apply_route_selection_locked(st, exclude, selected, discovered_origin)
4577 }
4578 }
4579
4580 #[allow(unused_mut)]
4581 fn register_end_to_end_reliable_tx(
4582 &self,
4583 data: &RouterItem,
4584 destinations: Option<BTreeMap<u64, RouterSideId>>,
4585 ) -> TelemetryResult<bool> {
4586 let packet_id = Self::get_hash(data);
4587 let now_ms = self.clock.now_ms();
4588 let ty = match data {
4589 RouterItem::Packet(pkt) => pkt.data_type(),
4590 RouterItem::Packed(bytes) => wire_format::peek_routing_envelope(bytes.as_ref())?.ty,
4591 };
4592 let mut st = self.state.lock();
4593 #[cfg(feature = "discovery")]
4594 let mut pending_destinations = match destinations {
4595 Some(destinations) => destinations,
4596 None => self.expected_end_to_end_destinations_locked(&st, data, None, false)?,
4597 };
4598 #[cfg(not(feature = "discovery"))]
4599 let mut pending_destinations = destinations.unwrap_or_default();
4600 let frozen = match data {
4604 RouterItem::Packet(pkt) => !pkt.wire_target_senders().is_empty(),
4605 RouterItem::Packed(bytes) => {
4606 wire_format::peek_routing_envelope(bytes)?
4607 .target_senders()
4608 .len()
4609 != 0
4610 }
4611 };
4612 self.filter_trackable_end_to_end_destinations_locked(
4613 &st,
4614 ty,
4615 &mut pending_destinations,
4616 frozen,
4617 );
4618 let tracked_destinations = !pending_destinations.is_empty();
4619 if let Some(existing) = st.end_to_end_reliable_tx.get(&packet_id) {
4620 return Ok(!existing.waiting_for_predecessor);
4621 }
4622 if st.end_to_end_reliable_tx.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4624 return Err(TelemetryError::HandlerError(
4625 "end-to-end reliable window full",
4626 ));
4627 }
4628 let ordered_type =
4629 matches!(reliable_mode(ty), crate::ReliableMode::Ordered).then_some(ty.as_u32());
4630 let waiting_for_predecessor = ordered_type.is_some()
4631 && st
4632 .end_to_end_reliable_tx
4633 .values()
4634 .any(|sent| sent.ordered_type == ordered_type);
4635 #[cfg(feature = "std")]
4636 if reliable_trace_enabled() {
4637 eprintln!(
4638 "[sedsnet-reliable] register time={now_ms} type={} id={packet_id:#x} waiting={waiting_for_predecessor} targets={pending_destinations:?}",
4639 ty.as_u32()
4640 );
4641 }
4642 let mut retained = data.clone();
4643 #[cfg(feature = "compact-packet-store")]
4644 retained.park_for_queue()?;
4645 Self::remember_end_to_end_reliable_tx_locked(&mut st, packet_id);
4646 st.end_to_end_reliable_tx.insert(
4647 packet_id,
4648 EndToEndReliableSent {
4649 ordered_type,
4650 waiting_for_predecessor,
4651 data: retained,
4652 pending_destinations,
4653 tracked_destinations,
4654 last_send_ms: now_ms,
4655 retries: 0,
4656 queued: false,
4657 },
4658 );
4659 Ok(!waiting_for_predecessor)
4660 }
4661
4662 fn release_ordered_successors(&self) -> TelemetryResult<()> {
4665 let ready = {
4666 let mut st = self.state.lock();
4667 let RouterInner {
4668 end_to_end_reliable_tx_order: order,
4669 end_to_end_reliable_tx: pending,
4670 ..
4671 } = &mut *st;
4672 let mut ready = Vec::new();
4673 for (index, id) in order.iter().copied().enumerate() {
4674 let Some(sent) = pending.get(&id) else {
4675 continue;
4676 };
4677 if !sent.waiting_for_predecessor {
4678 continue;
4679 }
4680 let Some(ty) = sent.ordered_type else {
4681 continue;
4682 };
4683 if order.iter().take(index).any(|earlier| {
4687 pending
4688 .get(earlier)
4689 .is_some_and(|sent| sent.ordered_type == Some(ty))
4690 }) {
4691 continue;
4692 }
4693 let sent = pending.get_mut(&id).expect("retained ordered entry");
4694 sent.waiting_for_predecessor = false;
4695 sent.last_send_ms = self.clock.now_ms();
4696 ready.push(id);
4697 }
4698 ready
4699 };
4700 for id in ready {
4701 self.queue_end_to_end_reliable_retransmit(id)?;
4702 }
4703 Ok(())
4704 }
4705
4706 #[cfg(feature = "discovery")]
4707 fn reconcile_end_to_end_reliable_destinations_locked(
4708 &self,
4709 st: &mut RouterInner,
4710 ) -> TelemetryResult<()> {
4711 if st.end_to_end_reliable_tx.is_empty() {
4712 return Ok(());
4713 }
4714 let active_destinations = self.active_end_to_end_destinations_locked(st);
4715 let packet_ids: Vec<u64> = st.end_to_end_reliable_tx.keys().copied().collect();
4716 let mut completed = Vec::new();
4717
4718 for packet_id in packet_ids {
4719 let Some(data) = st
4720 .end_to_end_reliable_tx
4721 .get(&packet_id)
4722 .map(|sent| sent.data.clone())
4723 else {
4724 continue;
4725 };
4726 let excluded = st
4727 .reliable_return_routes
4728 .get(&packet_id)
4729 .filter(|route| route.upstream_contract.is_some())
4730 .map(|route| route.side);
4731 let expected =
4732 self.expected_end_to_end_destinations_locked(st, &data, excluded, false)?;
4733 let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) else {
4734 continue;
4735 };
4736 if !sent.tracked_destinations {
4737 continue;
4738 }
4739 if sent.ordered_type.is_some() {
4741 for (sender, side) in &mut sent.pending_destinations {
4742 if let Some(next_side) = expected.get(sender).or_else(|| {
4743 active_destinations
4744 .get(sender)
4745 .filter(|side| Some(**side) != excluded)
4746 }) {
4747 *side = *next_side;
4748 }
4749 }
4750 continue;
4751 }
4752 sent.pending_destinations.retain(|sender_hash, side| {
4753 match (
4754 expected.get(sender_hash),
4755 active_destinations
4756 .get(sender_hash)
4757 .filter(|side| Some(**side) != excluded),
4758 ) {
4759 (Some(next_side), _) | (None, Some(next_side)) => {
4760 *side = *next_side;
4761 true
4762 }
4763 (None, None) => false,
4764 }
4765 });
4766 if sent.pending_destinations.is_empty() {
4767 completed.push(packet_id);
4768 }
4769 }
4770
4771 for packet_id in completed {
4772 st.end_to_end_reliable_tx.remove(&packet_id);
4773 }
4774
4775 Ok(())
4776 }
4777
4778 #[cfg(feature = "discovery")]
4779 fn active_end_to_end_destinations_locked(
4780 &self,
4781 st: &RouterInner,
4782 ) -> BTreeMap<u64, RouterSideId> {
4783 let now_ms = self.clock.now_ms();
4784 let mut out = BTreeMap::new();
4785 for (&side, route) in st.discovery_routes.iter() {
4786 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4787 continue;
4788 }
4789 for (sender_name, sender_state) in &route.announcers {
4790 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
4791 continue;
4792 }
4793 if Self::is_end_to_end_destination_sender(sender_name) {
4794 out.insert(Self::sender_hash(sender_name), side);
4795 }
4796 for board in sender_state.topology_boards.iter() {
4797 if !Self::is_end_to_end_destination_sender(&board.sender_id) {
4798 continue;
4799 }
4800 out.insert(Self::sender_hash(&board.sender_id), side);
4801 if out.len() >= runtime_reliable_max_end_to_end_pending().max(1) {
4802 return out;
4803 }
4804 }
4805 }
4806 }
4807 out
4808 }
4809
4810 fn side_supports_end_to_end_tracking_locked(st: &RouterInner, side: RouterSideId) -> bool {
4811 matches!(
4812 st.sides
4813 .get(side)
4814 .and_then(Option::as_ref)
4815 .map(|side| &side.tx_handler),
4816 Some(RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_))
4817 )
4818 }
4819
4820 fn filter_trackable_end_to_end_destinations_locked(
4821 &self,
4822 st: &RouterInner,
4823 ty: DataType,
4824 pending: &mut BTreeMap<u64, RouterSideId>,
4825 frozen: bool,
4826 ) {
4827 if DataType::try_named("HEARTBEAT") == Some(ty) {
4833 pending.clear();
4834 return;
4835 }
4836 if frozen {
4837 return;
4838 }
4839 let now_ms = self.clock.now_ms();
4840 pending.retain(|_, side| {
4841 Self::side_supports_end_to_end_tracking_locked(st, *side)
4842 && (st.managed_variable_types.contains(&ty.as_u32())
4843 || is_reliable_type(ty)
4844 || !self.side_has_multiple_announcers_locked(st, *side, now_ms))
4845 });
4846 }
4847
4848 #[cfg(feature = "discovery")]
4849 fn side_has_multiple_announcers_locked(
4850 &self,
4851 st: &RouterInner,
4852 side: RouterSideId,
4853 now_ms: u64,
4854 ) -> bool {
4855 st.discovery_routes
4856 .get(&side)
4857 .map(|route| {
4858 route
4859 .announcers
4860 .values()
4861 .filter(|sender| {
4862 now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
4863 })
4864 .take(2)
4865 .count()
4866 > 1
4867 })
4868 .unwrap_or(false)
4869 }
4870
4871 #[cfg(not(feature = "discovery"))]
4872 fn side_has_multiple_announcers_locked(
4873 &self,
4874 _st: &RouterInner,
4875 _side: RouterSideId,
4876 _now_ms: u64,
4877 ) -> bool {
4878 false
4879 }
4880
4881 fn queue_end_to_end_reliable_ack(
4882 &self,
4883 pkt: &Packet,
4884 called_from_queue: bool,
4885 ) -> TelemetryResult<()> {
4886 self.queue_end_to_end_reliable_ack_for_packet_id(
4887 pkt.packet_id(),
4888 pkt.sender(),
4889 called_from_queue,
4890 )
4891 }
4892
4893 fn queue_end_to_end_reliable_ack_for_packet_id(
4894 &self,
4895 packet_id: u64,
4896 original_sender: &str,
4897 called_from_queue: bool,
4898 ) -> TelemetryResult<()> {
4899 if !self.cfg.reliable_enabled() {
4900 return Ok(());
4901 }
4902 let original_sender_hash = {
4903 let st = self.state.lock();
4904 Self::sender_hash(&Self::canonical_sender_locked(&st, original_sender))
4905 };
4906 self.queue_end_to_end_reliable_ack_for_sender_hash(
4907 packet_id,
4908 original_sender_hash,
4909 called_from_queue,
4910 )
4911 }
4912
4913 fn queue_end_to_end_reliable_ack_for_sender_hash(
4914 &self,
4915 packet_id: u64,
4916 original_sender_hash: u64,
4917 called_from_queue: bool,
4918 ) -> TelemetryResult<()> {
4919 if !self.cfg.reliable_enabled() {
4920 return Ok(());
4921 }
4922 let ack_sender = self.encode_end_to_end_ack_sender();
4923 let ack = Packet::new(
4924 DataType::ReliableAck,
4925 message_meta(DataType::ReliableAck).endpoints_ref(),
4926 ack_sender.as_str(),
4927 self.packet_timestamp_ms(),
4928 crate::shared_bytes::convert(Self::encode_end_to_end_reliable_ack(packet_id)),
4929 )?;
4930 let local_sender = self.sender_arc();
4931 let ack = self.attach_wire_contract_to_item(
4935 RouterItem::Packet(ack),
4936 &[
4937 Self::sender_hash(local_sender.as_ref()),
4938 original_sender_hash,
4939 ],
4940 )?;
4941 let return_side = {
4945 let st = self.state.lock();
4946 st.reliable_return_routes
4947 .get(&packet_id)
4948 .map(|route| route.side)
4949 };
4950 let item = match return_side {
4951 Some(dst) => RouterTxItem::ToSide {
4952 src: None,
4953 dst,
4954 data: ack,
4955 },
4956 None => RouterTxItem::Broadcast(ack),
4957 };
4958 self.emit_internal_tx(item, true, called_from_queue)
4959 }
4960
4961 fn emit_internal_tx(
4962 &self,
4963 item: RouterTxItem,
4964 ignore_local: bool,
4965 called_from_queue: bool,
4966 ) -> TelemetryResult<()> {
4967 if called_from_queue {
4968 self.tx_queue_item_with_flags(item, ignore_local)
4969 } else {
4970 self.tx_item_impl(item, ignore_local, false)
4971 }
4972 }
4973
4974 fn emit_internal_tx_with_priority(
4975 &self,
4976 item: RouterTxItem,
4977 ignore_local: bool,
4978 priority: u8,
4979 called_from_queue: bool,
4980 ) -> TelemetryResult<()> {
4981 if called_from_queue {
4982 self.tx_queue_item_with_priority(item, ignore_local, priority)
4983 } else {
4984 self.tx_item_impl(item, ignore_local, false)
4985 }
4986 }
4987
4988 fn queue_end_to_end_reliable_retransmit(&self, packet_id: u64) -> TelemetryResult<()> {
4989 {
4990 let mut st = self.state.lock();
4991 let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) else {
4992 return Ok(());
4993 };
4994 if sent.queued {
4995 return Ok(());
4996 }
4997 sent.queued = true;
4998 }
4999 let result = self.tx_queue_item_with_priority(
5000 RouterTxItem::EndToEndReplay { packet_id },
5001 true,
5002 Self::router_item_priority_bumped(DataType::ReliableAck),
5003 );
5004 if result.is_err()
5005 && let Some(sent) = self.state.lock().end_to_end_reliable_tx.get_mut(&packet_id)
5006 {
5007 sent.queued = false;
5008 }
5009 result
5010 }
5011
5012 fn end_to_end_retransmit_sides(
5013 &self,
5014 packet_id: u64,
5015 ) -> Option<(RouterItem, Vec<RouterSideId>)> {
5016 let mut st = self.state.lock();
5017 let (data, tracked_destinations, mut sides) = {
5018 let sent = st.end_to_end_reliable_tx.get_mut(&packet_id)?;
5019 #[cfg(feature = "std")]
5020 if reliable_trace_enabled() {
5021 eprintln!(
5022 "[sedsnet-reliable] replay time={} id={packet_id:#x} retries={} targets={:?}",
5023 self.clock.now_ms(),
5024 sent.retries,
5025 sent.pending_destinations
5026 );
5027 }
5028 sent.queued = false;
5029 sent.last_send_ms = self.clock.now_ms();
5030 let data = sent.data.clone();
5031 let tracked_destinations = sent.tracked_destinations;
5032 let sides: Vec<RouterSideId> = sent.pending_destinations.values().copied().collect();
5033 (data, tracked_destinations, sides)
5034 };
5035 if tracked_destinations && sides.is_empty() {
5036 st.end_to_end_reliable_tx.remove(&packet_id);
5037 return None;
5038 }
5039 crate::small_sort::sort(&mut sides);
5040 sides.dedup();
5041 Some((data, sides))
5042 }
5043
5044 fn item_type_priority(data: &RouterItem) -> TelemetryResult<(DataType, u8)> {
5045 let (ty, control) = match data {
5046 RouterItem::Packet(pkt) => (
5047 pkt.data_type(),
5048 pkt.payload().len() <= 16
5049 && pkt.endpoints().len() <= 3
5050 && crate::small_frozen_control(
5051 pkt.data_type(),
5052 pkt.wire_target_senders().len(),
5053 pkt.wire_shape(),
5054 ),
5055 ),
5056 RouterItem::Packed(bytes) => {
5057 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
5058 let control = bytes.len() <= 128
5059 && bytes.first().is_some_and(|flags| flags & 0x11 == 0)
5060 && env.endpoints().take(4).count() <= 3
5061 && crate::small_frozen_control(
5062 env.ty,
5063 env.target_senders().len(),
5064 env.wire_shape,
5065 );
5066 (env.ty, control)
5067 }
5068 };
5069 let priority = crate::scheduler_priority(ty);
5070 Ok((ty, if control { priority.max(254) } else { priority }))
5071 }
5072
5073 fn router_item_priority(data: &RouterItem) -> TelemetryResult<u8> {
5074 Self::item_type_priority(data).map(|(_, priority)| priority)
5075 }
5076
5077 fn item_service_priority(&self, data: &RouterItem) -> TelemetryResult<u8> {
5078 let (ty, priority) = Self::item_type_priority(data)?;
5079 if self.is_managed_variable_type(ty) {
5080 Ok(crate::transport_priority(DataType::ManagedVariableValue))
5081 } else {
5082 Ok(priority)
5083 }
5084 }
5085
5086 #[inline]
5087 fn router_item_priority_bumped(ty: DataType) -> u8 {
5088 crate::scheduler_priority(ty).saturating_add(16)
5089 }
5090
5091 #[inline]
5092 fn is_side_tx_busy(err: &TelemetryError) -> bool {
5093 matches!(err, TelemetryError::Io("side tx busy"))
5094 }
5095
5096 #[cfg(feature = "timesync")]
5097 fn timesync_has_usable_time_locked(st: &TimeSyncRuntime, now_mono_ns: u64) -> bool {
5098 st.disciplined_clock.read_unix_ms(now_mono_ns).is_some()
5099 || st
5100 .clock
5101 .current_time(now_mono_ns)
5102 .and_then(|reading| reading.unix_time_ms)
5103 .is_some()
5104 }
5105
5106 #[cfg(feature = "timesync")]
5107 fn reconcile_pending_timesync_request_locked(
5108 st: &mut TimeSyncRuntime,
5109 leader: &Option<TimeSyncLeader>,
5110 now_ms: u64,
5111 ) {
5112 let active_remote = match leader {
5113 Some(TimeSyncLeader::Remote(remote)) => Some(remote.sender.as_str()),
5114 _ => None,
5115 };
5116 let should_clear = st
5117 .pending_request
5118 .as_ref()
5119 .is_some_and(|pending| Some(pending.source.as_str()) != active_remote);
5120 if should_clear {
5121 st.pending_request = None;
5122 st.next_request_mono_ms = now_ms;
5123 }
5124 }
5125
5126 #[inline]
5128 fn enqueue_to_sides(
5129 &self,
5130 data: RouterItem,
5131 exclude: Option<RouterSideId>,
5132 ignore_local: bool,
5133 ) -> TelemetryResult<()> {
5134 let Some(data) = self.clear_intermediate_hop_targets(data, true)? else {
5135 return Ok(());
5136 };
5137 let plan = self.remote_side_plan(&data, exclude)?;
5138 let mut st = self.state.lock();
5139 let ty = Self::item_data_type(&data)?;
5140 let priority = if st.managed_variable_types.contains(&ty.as_u32()) {
5141 crate::transport_priority(DataType::ManagedVariableValue)
5142 } else {
5143 Self::router_item_priority(&data)?
5144 };
5145
5146 let RemoteSidePlan::Target(sides) = plan;
5147 for idx in sides {
5148 st.push_transmit(TxQueued {
5149 item: RouterTxItem::ToSide {
5150 src: exclude,
5151 dst: idx,
5152 data: data.clone(),
5153 },
5154 ignore_local,
5155 priority,
5156 retry_after_ms: 0,
5157 })?;
5158 }
5159
5160 Ok(())
5161 }
5162
5163 fn relay_send(
5164 &self,
5165 data: RouterItem,
5166 src: Option<RouterSideId>,
5167 called_from_queue: bool,
5168 ) -> TelemetryResult<()> {
5169 if called_from_queue {
5170 return self.enqueue_to_sides(data, src, true);
5171 }
5172
5173 let Some(data) = self.clear_intermediate_hop_targets(data, false)? else {
5174 return Ok(());
5175 };
5176 let RemoteSidePlan::Target(sides) = self.remote_side_plan(&data, src)?;
5177 for side in sides {
5178 self.tx_item_impl(
5179 RouterTxItem::ToSide {
5180 src,
5181 dst: side,
5182 data: data.clone(),
5183 },
5184 true,
5185 false,
5186 )?;
5187 }
5188
5189 Ok(())
5190 }
5191
5192 fn item_route_info(&self, data: &RouterItem) -> TelemetryResult<(Vec<DataEndpoint>, DataType)> {
5193 match data {
5194 RouterItem::Packet(pkt) => {
5195 pkt.validate()?;
5196 let mut eps = pkt.endpoints().to_vec();
5197 crate::small_sort::sort(&mut eps);
5198 eps.dedup();
5199 Ok((eps, pkt.data_type()))
5200 }
5201 RouterItem::Packed(bytes) => {
5202 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
5203 let mut eps: Vec<DataEndpoint> = env.endpoints().collect();
5204 crate::small_sort::sort(&mut eps);
5205 eps.dedup();
5206 Ok((eps, env.ty))
5207 }
5208 }
5209 }
5210
5211 fn unpack_packet_for_role(&self, bytes: &[u8]) -> TelemetryResult<Packet> {
5212 let managed = crate::memory_admission::supports_work_kinds()
5213 && wire_format::scheduling_type(bytes).is_some_and(|ty| {
5214 !is_internal_control_type(ty) && self.is_managed_variable_type(ty)
5215 });
5216 wire_format::unpack_packet_with_role(bytes, managed)
5217 }
5218
5219 fn item_data_type(data: &RouterItem) -> TelemetryResult<DataType> {
5220 match data {
5221 RouterItem::Packet(pkt) => Ok(pkt.data_type()),
5222 RouterItem::Packed(bytes) => Ok(wire_format::peek_routing_envelope(bytes.as_ref())?.ty),
5223 }
5224 }
5225
5226 fn e2e_crypto_supported(&self) -> bool {
5227 #[cfg(feature = "cryptography")]
5228 {
5229 self.cfg.e2e_encryption() != RouterE2eEncryptionMode::Disabled
5230 && crate::crypto::registered_crypto_available()
5231 }
5232 #[cfg(not(feature = "cryptography"))]
5233 {
5234 false
5235 }
5236 }
5237
5238 fn should_require_e2e_for_type(&self, ty: DataType) -> bool {
5239 if is_internal_control_type(ty) {
5240 return false;
5241 }
5242 match self.cfg.e2e_encryption() {
5243 RouterE2eEncryptionMode::Disabled => {
5244 message_e2e_encryption_policy(ty) == E2eEncryptionPolicy::RequireOn
5245 }
5246 RouterE2eEncryptionMode::RequiredOnly => {
5247 message_e2e_encryption_policy(ty) == E2eEncryptionPolicy::RequireOn
5248 }
5249 RouterE2eEncryptionMode::Preferred => matches!(
5250 message_e2e_encryption_policy(ty),
5251 E2eEncryptionPolicy::PreferOn | E2eEncryptionPolicy::RequireOn
5252 ),
5253 RouterE2eEncryptionMode::ForceAll => true,
5254 }
5255 }
5256
5257 fn ensure_e2e_policy_supported_for_type(&self, ty: DataType) -> TelemetryResult<()> {
5258 if self.should_require_e2e_for_type(ty) && !self.e2e_crypto_supported() {
5259 return Err(TelemetryError::BadArg);
5260 }
5261 Ok(())
5262 }
5263
5264 #[cfg(feature = "cryptography")]
5265 fn e2e_seal_config_for_type(&self, ty: DataType) -> Option<wire_format::E2eSealConfig> {
5266 if self.should_require_e2e_for_type(ty) && self.e2e_crypto_supported() {
5267 Some(wire_format::E2eSealConfig {
5268 key_id: self.cfg.e2e_key_id(),
5269 })
5270 } else {
5271 None
5272 }
5273 }
5274
5275 #[inline]
5276 fn pack_packet_for_router(
5277 &self,
5278 pkt: &Packet,
5279 reliable: Option<wire_format::ReliableHeader>,
5280 ) -> TelemetryResult<crate::SharedBytes> {
5281 #[cfg(feature = "cryptography")]
5282 if let Some(e2e) = self.e2e_seal_config_for_type(pkt.data_type()) {
5283 return (wire_format::pack_packet_with_wire_contract_e2e(
5284 pkt,
5285 reliable,
5286 pkt.wire_shape(),
5287 pkt.wire_target_senders(),
5288 e2e,
5289 ))
5290 .map(crate::shared_bytes::convert);
5291 }
5292 Ok(match reliable {
5293 Some(hdr) => {
5294 crate::shared_bytes::convert(wire_format::pack_packet_with_reliable(pkt, hdr))
5295 }
5296 None => crate::shared_bytes::convert(wire_format::pack_packet(pkt)),
5297 })
5298 }
5299
5300 #[inline]
5301 fn pack_packet_for_contract(
5302 &self,
5303 pkt: &Packet,
5304 reliable: Option<wire_format::ReliableHeader>,
5305 shape: Option<MessageElement>,
5306 target_senders: &[u64],
5307 ) -> TelemetryResult<crate::SharedBytes> {
5308 #[cfg(feature = "cryptography")]
5309 if let Some(e2e) = self.e2e_seal_config_for_type(pkt.data_type()) {
5310 return (wire_format::pack_packet_with_wire_contract_e2e(
5311 pkt,
5312 reliable,
5313 shape,
5314 target_senders,
5315 e2e,
5316 ))
5317 .map(crate::shared_bytes::convert);
5318 }
5319 wire_format::pack_packet_with_wire_contract(pkt, reliable, shape, target_senders)
5320 .map(crate::shared_bytes::convert)
5321 }
5322
5323 #[cfg(feature = "cryptography")]
5324 #[inline]
5325 fn prepare_packed_for_remote(
5326 &self,
5327 bytes: crate::SharedBytes,
5328 reliable_override: Option<Option<wire_format::ReliableHeader>>,
5329 ) -> TelemetryResult<crate::SharedBytes> {
5330 let frame = wire_format::peek_frame_info(bytes.as_ref())?;
5331 if frame.ack_only() || self.e2e_seal_config_for_type(frame.envelope.ty).is_none() {
5332 return Ok(bytes);
5333 }
5334 let reliable = reliable_override.unwrap_or(frame.reliable);
5335 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
5336 self.pack_packet_for_contract(
5337 &pkt,
5338 reliable,
5339 frame.envelope.wire_shape,
5340 &frame.envelope.target_senders,
5341 )
5342 }
5343
5344 fn item_requires_end_to_end_tracking(
5348 &self,
5349 data: &RouterItem,
5350 ty: DataType,
5351 ) -> TelemetryResult<bool> {
5352 if is_reliable_type(ty) || self.is_managed_variable_type(ty) {
5353 return Ok(true);
5354 }
5355 match data {
5356 RouterItem::Packet(packet) => Ok(!packet.wire_target_senders().is_empty()),
5357 RouterItem::Packed(bytes) => {
5358 let frame = wire_format::peek_routing_frame_info(bytes.as_ref())?;
5359 Ok(frame.reliable.is_some() || frame.envelope.target_senders().len() != 0)
5360 }
5361 }
5362 }
5363
5364 fn item_target_senders<'a>(
5365 &self,
5366 data: &'a RouterItem,
5367 ) -> TelemetryResult<alloc::borrow::Cow<'a, [u64]>> {
5368 match data {
5369 RouterItem::Packet(pkt) => Ok(alloc::borrow::Cow::Borrowed(pkt.wire_target_senders())),
5370 RouterItem::Packed(bytes) => {
5371 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
5372 if env.target_senders().len() == 0 {
5373 Ok(alloc::borrow::Cow::Borrowed(&[]))
5374 } else {
5375 Ok(alloc::borrow::Cow::Owned(env.target_senders().collect()))
5376 }
5377 }
5378 }
5379 }
5380
5381 fn item_targets_local_sender(&self, data: &RouterItem) -> TelemetryResult<bool> {
5382 let local_hash = Self::sender_hash(self.sender_arc().as_ref());
5383 match data {
5384 RouterItem::Packet(pkt) => Ok(pkt.wire_target_senders().is_empty()
5385 || pkt.wire_target_senders().contains(&local_hash)),
5386 RouterItem::Packed(bytes) => {
5387 let env = wire_format::peek_routing_envelope(bytes.as_ref())?;
5388 Ok(env.target_senders().len() == 0
5389 || env.target_senders().any(|target| target == local_hash))
5390 }
5391 }
5392 }
5393
5394 fn item_targets_router_as_intermediate_hop(
5395 &self,
5396 data: &RouterItem,
5397 endpoints: &[DataEndpoint],
5398 ) -> TelemetryResult<bool> {
5399 let local_target = match data {
5402 RouterItem::Packet(pkt) => {
5403 if pkt.wire_target_senders().is_empty() || is_internal_control_type(pkt.data_type())
5404 {
5405 return Ok(false);
5406 }
5407 pkt.wire_target_senders()
5408 .contains(&Self::sender_hash(self.sender_arc().as_ref()))
5409 }
5410 RouterItem::Packed(bytes) => {
5411 let env = wire_format::peek_routing_envelope(bytes)?;
5412 if env.target_senders().len() == 0 || is_internal_control_type(env.ty) {
5413 return Ok(false);
5414 }
5415 let local = Self::sender_hash(self.sender_arc().as_ref());
5416 env.target_senders().any(|owner| owner == local)
5417 }
5418 };
5419 Ok(local_target
5420 && !endpoints.iter().copied().any(|endpoint| {
5421 self.endpoint_has_packet_handler(endpoint)
5422 || self.endpoint_has_packed_handler(endpoint)
5423 }))
5424 }
5425
5426 fn clear_intermediate_hop_targets(
5427 &self,
5428 data: RouterItem,
5429 called_from_queue: bool,
5430 ) -> TelemetryResult<Option<RouterItem>> {
5431 let has_targets = match &data {
5435 RouterItem::Packet(pkt) => !pkt.wire_target_senders().is_empty(),
5436 RouterItem::Packed(bytes) => {
5437 wire_format::peek_routing_envelope(bytes)?
5438 .target_senders()
5439 .len()
5440 != 0
5441 }
5442 };
5443 if !has_targets {
5444 return Ok(Some(data));
5445 }
5446 let (endpoints, _) = self.item_route_info(&data)?;
5447 if !self.item_targets_router_as_intermediate_hop(&data, &endpoints)? {
5448 return Ok(Some(data));
5449 }
5450 let pkt = match &data {
5451 RouterItem::Packet(pkt) => pkt.clone(),
5452 RouterItem::Packed(bytes) => self.unpack_packet_for_role(bytes.as_ref())?,
5453 };
5454 let reliable = match &data {
5455 RouterItem::Packet(_) => None,
5456 RouterItem::Packed(bytes) => wire_format::peek_frame_info(bytes.as_ref())?.reliable,
5457 };
5458 let shape = match &data {
5459 RouterItem::Packet(pkt) => pkt.wire_shape(),
5460 RouterItem::Packed(bytes) => {
5461 wire_format::peek_routing_envelope(bytes.as_ref())?.wire_shape
5462 }
5463 };
5464 let upstream_id = Self::get_hash(&data);
5465 #[cfg(feature = "discovery")]
5466 let destinations = {
5467 let st = self.state.lock();
5468 let upstream = st
5469 .reliable_return_routes
5470 .get(&upstream_id)
5471 .map(|route| route.side);
5472 let mut pending =
5473 self.expected_end_to_end_destinations_locked(&st, &data, upstream, true)?;
5474 if pending.is_empty() {
5475 pending =
5476 self.expected_end_to_end_destinations_locked(&st, &data, upstream, false)?;
5477 }
5478 pending
5479 };
5480 #[cfg(feature = "discovery")]
5481 let targets: Vec<u64> = destinations.keys().copied().collect();
5482 #[cfg(not(feature = "discovery"))]
5483 let targets: Vec<u64> = Vec::new();
5484 #[cfg(feature = "discovery")]
5485 if targets.is_empty() {
5486 if self.packet_has_local_delivery(&pkt) {
5487 self.queue_end_to_end_reliable_ack_for_packet_id(
5488 upstream_id,
5489 pkt.sender(),
5490 called_from_queue,
5491 )?;
5492 return Ok(Some(data));
5493 }
5494 if !is_reliable_type(pkt.data_type()) && reliable.is_none() {
5495 return Err(TelemetryError::Io("no discovered route"));
5496 }
5497 }
5498 let forwarded = self.pack_packet_for_contract(&pkt, reliable, shape, &targets)?;
5502 let downstream_id = wire_format::packet_id_from_wire(forwarded.as_ref())?;
5503 let forwarded = RouterItem::Packed(forwarded);
5504 #[cfg(feature = "discovery")]
5505 let already_pending = self
5506 .state
5507 .lock()
5508 .end_to_end_reliable_tx
5509 .contains_key(&downstream_id);
5510 #[cfg(feature = "discovery")]
5511 let ready = self.register_end_to_end_reliable_tx(&forwarded, Some(destinations))?;
5512 #[cfg(not(feature = "discovery"))]
5513 let (ready, already_pending) = {
5514 let _ = called_from_queue;
5515 (true, false)
5516 };
5517 #[cfg(feature = "std")]
5518 if reliable_trace_enabled() {
5519 eprintln!(
5520 "[sedsnet-reliable] proxy retain upstream={upstream_id:#x} downstream={downstream_id:#x} existing={already_pending} ready={ready}"
5521 );
5522 }
5523 {
5524 let mut st = self.state.lock();
5525 let Some(mut route) = st.reliable_return_routes.get(&upstream_id).cloned() else {
5526 st.end_to_end_reliable_tx.remove(&downstream_id);
5527 return Err(TelemetryError::Io("no reliable return route"));
5528 };
5529 route.upstream_contract = Some((
5530 upstream_id,
5531 Self::sender_hash(&Self::canonical_sender_locked(&st, pkt.sender())),
5532 ));
5533 if !Self::remember_reliable_return_route_locked(&mut st, downstream_id) {
5534 st.end_to_end_reliable_tx.remove(&downstream_id);
5535 return Err(TelemetryError::Io("reliable return route window full"));
5536 }
5537 st.reliable_return_routes.insert(downstream_id, route);
5538 }
5539 Ok((ready && !already_pending).then_some(forwarded))
5540 }
5541
5542 #[cfg(feature = "discovery")]
5543 fn side_matches_target_senders_locked(
5544 st: &RouterInner,
5545 side: RouterSideId,
5546 target_senders: &[u64],
5547 now_ms: u64,
5548 ) -> bool {
5549 st.discovery_routes
5550 .get(&side)
5551 .map(|route| {
5552 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5553 return false;
5554 }
5555 route.announcers.iter().any(|(announcer, sender_state)| {
5556 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
5557 return false;
5558 }
5559 target_senders.contains(&Self::sender_hash(announcer))
5560 || sender_state
5561 .advertised_address
5562 .is_some_and(|address| target_senders.contains(&u64::from(address)))
5563 || sender_state.topology_boards.iter().any(|board| {
5564 target_senders.contains(&Self::sender_hash(&board.sender_id))
5565 })
5566 })
5567 })
5568 .unwrap_or(false)
5569 }
5570
5571 fn attach_wire_contract_to_item(
5572 &self,
5573 data: RouterItem,
5574 target_senders: &[u64],
5575 ) -> TelemetryResult<RouterItem> {
5576 match data {
5577 RouterItem::Packet(pkt) => {
5578 let reliable = if is_reliable_type(pkt.data_type()) {
5579 Some(wire_format::ReliableHeader {
5580 flags: wire_format::RELIABLE_FLAG_UNSEQUENCED,
5581 seq: 0,
5582 ack: 0,
5583 })
5584 } else {
5585 None
5586 };
5587 let bytes = self.pack_packet_for_contract(
5588 &pkt,
5589 reliable,
5590 Some(message_meta(pkt.data_type()).element),
5591 target_senders,
5592 )?;
5593 Ok(RouterItem::Packed(bytes))
5594 }
5595 RouterItem::Packed(bytes) => Ok(RouterItem::Packed(bytes)),
5596 }
5597 }
5598
5599 fn endpoints_are_link_local_only(eps: &[DataEndpoint]) -> bool {
5600 !eps.is_empty() && eps.iter().all(|ep| ep.is_link_local_only())
5601 }
5602
5603 fn should_route_remote(
5604 &self,
5605 data: &RouterItem,
5606 exclude: Option<RouterSideId>,
5607 ) -> TelemetryResult<bool> {
5608 #[cfg(feature = "discovery")]
5609 {
5610 let RemoteSidePlan::Target(sides) = self.remote_side_plan(data, exclude)?;
5611 Ok(!sides.is_empty())
5612 }
5613
5614 #[cfg(not(feature = "discovery"))]
5615 {
5616 let (eps, ty) = self.item_route_info(data)?;
5617 if !(has_nonlocal_endpoint(&eps, &self.cfg) || force_remote_for_type(ty)) {
5618 return Ok(false);
5619 }
5620 let st = self.state.lock();
5621 Ok(!self
5622 .eligible_side_ids_locked(
5623 &st,
5624 exclude,
5625 Some(ty),
5626 Self::endpoints_are_link_local_only(&eps),
5627 )
5628 .is_empty())
5629 }
5630 }
5631
5632 #[cfg(feature = "discovery")]
5633 fn has_explicit_route_policy_locked(
5634 st: &RouterInner,
5635 src: Option<RouterSideId>,
5636 ty: DataType,
5637 ) -> bool {
5638 st.route_overrides
5639 .keys()
5640 .any(|(route_src, _)| *route_src == src)
5641 || Self::has_typed_route_overrides_locked(st, src, ty)
5642 }
5643
5644 fn remote_side_plan(
5645 &self,
5646 data: &RouterItem,
5647 exclude: Option<RouterSideId>,
5648 ) -> TelemetryResult<RemoteSidePlan> {
5649 #[cfg(feature = "discovery")]
5650 {
5651 let (eps, ty) = self.item_route_info(data)?;
5652 let target_senders = self.item_target_senders(data)?;
5653 let resolve_beyond_local_hop =
5654 self.item_targets_router_as_intermediate_hop(data, &eps)?;
5655 let preferred_packet_id = Self::reliable_control_target_packet_id(data)?;
5656 let routing_target_senders: &[u64] = if preferred_packet_id.is_some() {
5657 target_senders.get(1..).unwrap_or(&[])
5660 } else if resolve_beyond_local_hop {
5661 &[]
5662 } else {
5663 target_senders.as_ref()
5664 };
5665 if discovery::is_discovery_type(ty) {
5666 let mut st = self.state.lock();
5667 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5668 return Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5669 &mut st,
5670 exclude,
5671 sides,
5672 RouteSelectionOrigin::Flood,
5673 )));
5674 }
5675 #[cfg(feature = "timesync")]
5676 if ty == DataType::TimeSyncAnnounce {
5677 let mut st = self.state.lock();
5684 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5685 let sides =
5686 Self::filter_timesync_sides_locked(&mut st, ty, self.clock.now_ms(), sides);
5687 return Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5688 &mut st,
5689 exclude,
5690 sides,
5691 RouteSelectionOrigin::Flood,
5692 )));
5693 }
5694 let mut st = self.state.lock();
5695 let now_ms = self.clock.now_ms();
5700 let has_remote_variable_owner = st.managed_variable_types.contains(&ty.as_u32())
5701 && st.discovery_routes.iter().any(|(&side, route)| {
5702 exclude != Some(side)
5703 && now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
5704 && route.reachable_network_variables.contains(&ty)
5705 });
5706 if !(has_nonlocal_endpoint(&eps, &self.cfg)
5707 || force_remote_for_type(ty)
5708 || !target_senders.is_empty()
5709 || has_remote_variable_owner)
5710 {
5711 return Ok(RemoteSidePlan::Target(Vec::new()));
5712 }
5713
5714 #[cfg(feature = "timesync")]
5715 let preferred_timesync_source = self.preferred_timesync_route_source(data, ty)?;
5716 #[cfg(not(feature = "timesync"))]
5717 let preferred_timesync_source: Option<String> = None;
5718
5719 if let Some(packet_id) = preferred_packet_id {
5720 let target_side = st
5721 .reliable_return_routes
5722 .get(&packet_id)
5723 .map(|route| route.side);
5724 if let Some(side) = target_side
5725 .filter(|side| self.route_allowed_locked(&st, exclude, Some(ty), *side))
5726 {
5727 #[cfg(feature = "timesync")]
5728 if !Self::timesync_allowed_for_side_locked(
5729 &mut st,
5730 side,
5731 ty,
5732 self.clock.now_ms(),
5733 ) {
5734 return Ok(RemoteSidePlan::Target(Vec::new()));
5735 }
5736 return Ok(RemoteSidePlan::Target(vec![side]));
5737 }
5738 }
5744 let restrict_link_local = Self::endpoints_are_link_local_only(&eps);
5745 let prefer_best_overlap = is_reliable_type(ty)
5746 && routing_target_senders.is_empty()
5747 && preferred_packet_id.is_none();
5748 if st.discovery_routes.is_empty() {
5749 let mut fallback =
5750 self.eligible_side_ids_locked(&st, exclude, Some(ty), restrict_link_local);
5751 #[cfg(feature = "timesync")]
5752 {
5753 fallback = Self::filter_timesync_sides_locked(
5754 &mut st,
5755 ty,
5756 self.clock.now_ms(),
5757 fallback,
5758 );
5759 }
5760 return Ok(RemoteSidePlan::Target(if fallback.len() == 1 {
5761 fallback
5762 } else {
5763 Vec::new()
5764 }));
5765 }
5766 let mut matches = self.discovered_route_candidates_locked(
5767 &st,
5768 exclude,
5769 ty,
5770 &eps,
5771 routing_target_senders,
5772 prefer_best_overlap,
5773 preferred_timesync_source.as_deref(),
5774 );
5775 #[cfg(feature = "timesync")]
5776 {
5777 matches =
5778 Self::filter_timesync_matches_locked(&mut st, ty, self.clock.now_ms(), matches);
5779 }
5780 let has_distributed_variable_owners = matches.iter().any(|candidate| {
5786 st.discovery_routes
5787 .get(&candidate.side)
5788 .is_some_and(|route| route.reachable_network_variables.contains(&ty))
5789 });
5790 if DataType::try_named("HEARTBEAT") != Some(ty) && !has_distributed_variable_owners {
5791 Self::retain_shortest_discovery_candidates_locked(
5792 &st,
5793 &mut matches,
5794 &eps,
5795 routing_target_senders,
5796 self.clock.now_ms(),
5797 );
5798 }
5799
5800 if !matches.is_empty() {
5801 Ok(RemoteSidePlan::Target(
5802 self.select_discovered_candidate_sides_locked(
5803 &mut st,
5804 exclude,
5805 ty,
5806 routing_target_senders,
5807 prefer_best_overlap,
5808 matches,
5809 ),
5810 ))
5811 } else if preferred_packet_id.is_some() {
5812 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5818 Ok(RemoteSidePlan::Target(
5819 if exclude.is_some() && sides.len() == 1 {
5820 sides
5821 } else {
5822 Vec::new()
5823 },
5824 ))
5825 } else if prefer_best_overlap {
5826 Ok(RemoteSidePlan::Target(Vec::new()))
5827 } else {
5828 if Self::has_explicit_route_policy_locked(&st, exclude, ty) {
5829 let mut sides =
5830 self.eligible_side_ids_locked(&st, exclude, Some(ty), restrict_link_local);
5831 #[cfg(feature = "timesync")]
5832 {
5833 sides = Self::filter_timesync_sides_locked(
5834 &mut st,
5835 ty,
5836 self.clock.now_ms(),
5837 sides,
5838 );
5839 }
5840 Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5841 &mut st,
5842 exclude,
5843 sides,
5844 RouteSelectionOrigin::Flood,
5845 )))
5846 } else {
5847 Ok(RemoteSidePlan::Target(Vec::new()))
5848 }
5849 }
5850 }
5851 #[cfg(not(feature = "discovery"))]
5852 {
5853 let (_, ty) = self.item_route_info(data)?;
5854 let mut st = self.state.lock();
5855 if let Some(packet_id) = Self::reliable_control_target_packet_id(data)? {
5856 let target_side = st
5857 .reliable_return_routes
5858 .get(&packet_id)
5859 .map(|route| route.side);
5860 if let Some(side) = target_side
5861 .filter(|side| self.route_allowed_locked(&st, exclude, Some(ty), *side))
5862 {
5863 return Ok(RemoteSidePlan::Target(vec![side]));
5864 }
5865 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5866 return Ok(RemoteSidePlan::Target(
5867 if exclude.is_some() && sides.len() == 1 {
5868 sides
5869 } else {
5870 Vec::new()
5871 },
5872 ));
5873 }
5874 let sides = self.eligible_side_ids_locked(&st, exclude, Some(ty), false);
5875 Ok(RemoteSidePlan::Target(self.apply_route_selection_locked(
5876 &mut st,
5877 exclude,
5878 sides,
5879 RouteSelectionOrigin::Flood,
5880 )))
5881 }
5882 }
5883
5884 #[cfg(feature = "discovery")]
5885 fn local_discovery_endpoints(&self, st: &RouterInner) -> Vec<DataEndpoint> {
5886 let mut eps: Vec<DataEndpoint> = self.cfg.handlers.iter().map(|h| h.endpoint).collect();
5887 let _ = st;
5893 #[cfg(feature = "timesync")]
5894 if self.cfg.timesync_config().is_some() {
5895 eps.push(DataEndpoint::TimeSync);
5896 }
5897 eps.retain(|ep| !discovery::is_discovery_endpoint(*ep));
5898 crate::small_sort::sort(&mut eps);
5899 eps.dedup();
5900 eps
5901 }
5902
5903 #[cfg(feature = "discovery")]
5904 fn local_discovery_timesync_sources(&self, now_ms: u64) -> Vec<String> {
5905 #[cfg(feature = "timesync")]
5906 {
5907 let st = self.timesync.lock();
5908 if let Some(tracker) = st.tracker.as_ref()
5909 && tracker.should_serve(
5910 now_ms,
5911 Self::timesync_has_usable_time_locked(&st, self.monotonic_now_ns()),
5912 )
5913 {
5914 return vec![self.sender_arc().to_string()];
5915 }
5916 }
5917 Vec::new()
5918 }
5919
5920 #[cfg(all(feature = "discovery", feature = "timesync"))]
5921 fn preferred_timesync_route_source(
5922 &self,
5923 data: &RouterItem,
5924 ty: DataType,
5925 ) -> TelemetryResult<Option<String>> {
5926 if !matches!(
5927 ty,
5928 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
5929 ) {
5930 return Ok(None);
5931 }
5932
5933 match data {
5934 RouterItem::Packet(pkt) => match ty {
5935 DataType::TimeSyncRequest => {
5936 let local_sender = self.sender_arc();
5937 if pkt.sender() == local_sender.as_ref() {
5938 Ok(self.timesync.lock().tracker.as_ref().and_then(|tracker| {
5939 tracker.current_source().map(|src| src.sender.clone())
5940 }))
5941 } else {
5942 Ok(None)
5943 }
5944 }
5945 DataType::TimeSyncAnnounce | DataType::TimeSyncResponse => {
5946 Ok(Some(pkt.sender().to_owned()))
5947 }
5948 _ => Ok(None),
5949 },
5950 RouterItem::Packed(bytes) => {
5951 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
5952 self.preferred_timesync_route_source(&RouterItem::Packet(pkt), ty)
5953 }
5954 }
5955 }
5956
5957 #[cfg(feature = "discovery")]
5958 fn note_discovery_topology_change_locked(st: &mut RouterInner, now_ms: u64) {
5959 st.discovery_cadence.on_topology_change(now_ms);
5960 for side_id in st
5965 .sides
5966 .iter()
5967 .enumerate()
5968 .filter_map(|(side_id, side)| side.as_ref().map(|_| side_id))
5969 {
5970 let throttle = st.discovery_side_throttle.entry(side_id).or_default();
5971 throttle.pending_incremental = true;
5972 throttle.incremental_retries_remaining = DISCOVERY_INCREMENTAL_RETRY_COUNT;
5973 }
5974 for transport in st.side_transport.values_mut() {
5978 transport.refresh_transmit_header_templates();
5979 }
5980 }
5981
5982 #[cfg(feature = "discovery")]
5983 fn sender_topology_board_mut<'a>(
5984 sender_state: &'a mut DiscoverySenderState,
5985 sender_id: &str,
5986 ) -> &'a mut TopologyBoardNode {
5987 if let Some(idx) = sender_state
5988 .topology_boards
5989 .iter()
5990 .position(|board| board.sender_id == sender_id)
5991 {
5992 return &mut sender_state.topology_boards[idx];
5993 }
5994 sender_state.topology_boards.push(TopologyBoardNode {
5995 sender_id: sender_id.to_string(),
5996 reachable_endpoints: Vec::new(),
5997 reachable_timesync_sources: Vec::new(),
5998 connections: Vec::new(),
5999 });
6000 sender_state
6001 .topology_boards
6002 .last_mut()
6003 .expect("board inserted above")
6004 }
6005
6006 #[cfg(feature = "discovery")]
6007 fn refresh_sender_topology_state(sender_state: &mut DiscoverySenderState) {
6008 discovery::normalize_topology_boards(&mut sender_state.topology_boards);
6009 let (mut reachable, mut reachable_timesync_sources) =
6010 discovery::summarize_topology_boards(&sender_state.topology_boards);
6011 reachable.extend(sender_state.advertised_reachable.iter().copied());
6012 crate::small_sort::sort(&mut reachable);
6013 reachable.dedup();
6014 reachable_timesync_sources.extend(
6015 sender_state
6016 .advertised_reachable_timesync_sources
6017 .iter()
6018 .cloned(),
6019 );
6020 reachable_timesync_sources.sort_unstable();
6021 reachable_timesync_sources.dedup();
6022 sender_state.reachable = reachable;
6023 sender_state.reachable_timesync_sources = reachable_timesync_sources;
6024 }
6025
6026 #[cfg(feature = "discovery")]
6027 fn recompute_discovery_side_state(route: &mut DiscoverySideState) {
6028 let mut reachable = Vec::new();
6029 let mut reachable_network_variables = Vec::new();
6030 let mut reachable_timesync_sources = Vec::new();
6031 let mut last_seen_ms = 0u64;
6032 for sender in route.announcers.values() {
6033 reachable.extend(sender.reachable.iter().copied());
6034 reachable_network_variables.extend(sender.reachable_network_variables.iter().copied());
6035 reachable_timesync_sources.extend(sender.reachable_timesync_sources.iter().cloned());
6036 last_seen_ms = last_seen_ms.max(sender.last_seen_ms);
6037 }
6038 reachable_network_variables
6039 .extend(route.requested_network_variables.iter().map(|(ty, _)| *ty));
6040 last_seen_ms = last_seen_ms.max(
6041 route
6042 .requested_network_variables
6043 .iter()
6044 .map(|(_, last_seen_ms)| *last_seen_ms)
6045 .max()
6046 .unwrap_or(0),
6047 );
6048 crate::small_sort::sort(&mut reachable);
6049 reachable.dedup();
6050 crate::small_sort::sort(&mut reachable_network_variables);
6051 reachable_network_variables.dedup();
6052 reachable_timesync_sources.sort_unstable();
6053 reachable_timesync_sources.dedup();
6054 route.reachable = reachable;
6055 route.reachable_network_variables = reachable_network_variables;
6056 route.reachable_timesync_sources = reachable_timesync_sources;
6057 route.last_seen_ms = last_seen_ms;
6058 }
6059
6060 #[cfg(feature = "discovery")]
6061 fn local_discovery_topology_board(
6062 &self,
6063 st: &RouterInner,
6064 now_ms: u64,
6065 link_local_enabled: bool,
6066 ) -> TopologyBoardNode {
6067 let mut reachable_endpoints = self.local_discovery_endpoints(st);
6068 if !link_local_enabled {
6069 reachable_endpoints.retain(|ep| !ep.is_link_local_only());
6070 }
6071 let mut connections = Vec::new();
6072 for route in st.discovery_routes.values() {
6073 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
6074 continue;
6075 }
6076 for (sender, sender_state) in route.announcers.iter() {
6077 if now_ms.saturating_sub(sender_state.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
6078 connections.push(sender.clone());
6079 }
6080 }
6081 }
6082 connections.sort_unstable();
6083 connections.dedup();
6084 let sender = self.sender_arc();
6085 TopologyBoardNode {
6086 sender_id: sender.to_string(),
6087 reachable_endpoints,
6088 reachable_timesync_sources: self.local_discovery_timesync_sources(now_ms),
6089 connections,
6090 }
6091 }
6092
6093 #[cfg(feature = "discovery")]
6094 fn advertised_discovery_topology_for_link_locked(
6095 &self,
6096 st: &RouterInner,
6097 now_ms: u64,
6098 link_local_enabled: bool,
6099 exclude_side: Option<RouterSideId>,
6100 ) -> Vec<TopologyBoardNode> {
6101 let mut boards = vec![self.local_discovery_topology_board(st, now_ms, link_local_enabled)];
6102 for (&route_side, route) in st.discovery_routes.iter() {
6103 if exclude_side == Some(route_side) {
6104 continue;
6105 }
6106 if now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
6107 continue;
6108 }
6109 for (announcer, sender_state) in route.announcers.iter() {
6110 if now_ms.saturating_sub(sender_state.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
6111 continue;
6112 }
6113 if sender_state.topology_boards.is_empty() {
6114 let sender = self.sender_arc();
6115 let fallback = TopologyBoardNode {
6116 sender_id: announcer.clone(),
6117 reachable_endpoints: sender_state.reachable.clone(),
6118 reachable_timesync_sources: sender_state.reachable_timesync_sources.clone(),
6119 connections: vec![sender.to_string()],
6120 };
6121 discovery::merge_topology_boards(&mut boards, core::slice::from_ref(&fallback));
6122 } else {
6123 discovery::merge_topology_boards(&mut boards, &sender_state.topology_boards);
6126 if let Some(board) = boards
6127 .iter_mut()
6128 .find(|board| board.sender_id == *announcer)
6129 {
6130 let sender = self.sender_arc();
6131 if !board.connections.iter().any(|peer| peer == sender.as_ref()) {
6132 board.connections.push(sender.to_string());
6133 }
6134 }
6135 }
6136 }
6137 }
6138 if !link_local_enabled {
6139 for board in &mut boards {
6140 board
6141 .reachable_endpoints
6142 .retain(|ep| !ep.is_link_local_only());
6143 }
6144 }
6145 discovery::normalize_topology_boards(&mut boards);
6146 boards
6147 }
6148
6149 #[cfg(feature = "discovery")]
6150 fn prune_discovery_routes_locked(st: &mut RouterInner, now_ms: u64) -> bool {
6151 let mut changed = false;
6154 st.discovery_routes.retain(|_, route| {
6155 let before_peers = route.announcers.len();
6156 let before_requests = route.requested_network_variables.len();
6157 route.announcers.retain(|_, sender| {
6158 now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6159 });
6160 route
6161 .requested_network_variables
6162 .retain(|(_, last_seen_ms)| {
6163 now_ms.saturating_sub(*last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6164 });
6165 if before_peers != route.announcers.len()
6166 || before_requests != route.requested_network_variables.len()
6167 {
6168 Self::recompute_discovery_side_state(route);
6169 changed = true;
6170 }
6171 let keep =
6172 !route.announcers.is_empty() || !route.requested_network_variables.is_empty();
6173 changed |= !keep;
6174 keep
6175 });
6176 changed
6177 }
6178
6179 #[cfg(feature = "discovery")]
6180 fn advertised_summaries_from_topology_locked(
6181 &self,
6182 st: &RouterInner,
6183 now_ms: u64,
6184 link_local_enabled: bool,
6185 exclude_side: Option<RouterSideId>,
6186 topology: &[TopologyBoardNode],
6187 ) -> (Vec<crate::DataEndpoint>, Vec<String>) {
6188 let (mut endpoints, mut sources) = discovery::summarize_topology_boards(topology);
6189 for (&side, route) in &st.discovery_routes {
6190 if exclude_side == Some(side)
6191 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
6192 {
6193 continue;
6194 }
6195 for peer in route.announcers.values() {
6196 if now_ms.saturating_sub(peer.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS {
6197 continue;
6198 }
6199 for endpoint in &peer.advertised_reachable {
6200 if !discovery::is_discovery_endpoint(*endpoint)
6201 && (link_local_enabled || !endpoint.is_link_local_only())
6202 && !endpoints.contains(endpoint)
6203 {
6204 endpoints.push(*endpoint);
6205 }
6206 }
6207 for source in &peer.advertised_reachable_timesync_sources {
6208 if !sources.contains(source) {
6209 sources.push(source.clone());
6210 }
6211 }
6212 }
6213 }
6214 endpoints.retain(|endpoint| {
6215 !discovery::is_discovery_endpoint(*endpoint)
6216 && (link_local_enabled || !endpoint.is_link_local_only())
6217 });
6218 crate::small_sort::sort(&mut endpoints);
6219 sources.sort_unstable();
6220 (endpoints, sources)
6221 }
6222
6223 #[cfg(feature = "discovery")]
6224 fn advertised_discovery_endpoints_for_link_locked(
6225 &self,
6226 st: &RouterInner,
6227 now_ms: u64,
6228 link_local_enabled: bool,
6229 exclude_side: Option<RouterSideId>,
6230 ) -> Vec<DataEndpoint> {
6231 let (mut reachable_endpoints, _) = discovery::summarize_topology_boards(
6232 &self.advertised_discovery_topology_for_link_locked(
6233 st,
6234 now_ms,
6235 link_local_enabled,
6236 exclude_side,
6237 ),
6238 );
6239 for (&side, route) in &st.discovery_routes {
6242 if exclude_side == Some(side)
6243 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
6244 {
6245 continue;
6246 }
6247 for sender in route.announcers.values() {
6248 if now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
6249 reachable_endpoints.extend(sender.advertised_reachable.iter().copied());
6250 }
6251 }
6252 }
6253 crate::small_sort::sort(&mut reachable_endpoints);
6254 reachable_endpoints.dedup();
6255 reachable_endpoints
6256 .into_iter()
6257 .filter(|ep| {
6258 !discovery::is_discovery_endpoint(*ep)
6259 && (link_local_enabled || !ep.is_link_local_only())
6260 })
6261 .collect()
6262 }
6263
6264 #[cfg(all(test, feature = "discovery"))]
6265 fn compact_summary_fixture(&self, endpoint: DataEndpoint) {
6266 let mut sender = DiscoverySenderState {
6267 advertised_reachable: vec![endpoint],
6268 advertised_reachable_timesync_sources: vec!["clock-behind-bridge".to_string()],
6269 last_seen_ms: self.clock.now_ms(),
6270 topology_boards: vec![TopologyBoardNode {
6271 sender_id: "bridge".to_string(),
6272 reachable_endpoints: Vec::new(),
6273 reachable_timesync_sources: Vec::new(),
6274 connections: Vec::new(),
6275 }],
6276 ..Default::default()
6277 };
6278 Self::refresh_sender_topology_state(&mut sender);
6279 let mut route = DiscoverySideState::default();
6280 route.announcers.insert("bridge".to_string(), sender);
6281 Self::recompute_discovery_side_state(&mut route);
6282 self.state.lock().discovery_routes.insert(0, route);
6283 }
6284
6285 #[cfg(feature = "discovery")]
6286 fn advertised_discovery_timesync_sources_for_link_locked(
6287 &self,
6288 st: &RouterInner,
6289 now_ms: u64,
6290 exclude_side: Option<RouterSideId>,
6291 ) -> Vec<String> {
6292 let (_, mut sources) = discovery::summarize_topology_boards(
6293 &self.advertised_discovery_topology_for_link_locked(st, now_ms, true, exclude_side),
6294 );
6295 for (&side, route) in &st.discovery_routes {
6296 if exclude_side == Some(side)
6297 || now_ms.saturating_sub(route.last_seen_ms) > DISCOVERY_ROUTE_TTL_MS
6298 {
6299 continue;
6300 }
6301 for sender in route.announcers.values() {
6302 if now_ms.saturating_sub(sender.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
6303 sources.extend(sender.advertised_reachable_timesync_sources.iter().cloned());
6304 }
6305 }
6306 }
6307 sources.sort_unstable();
6308 sources.dedup();
6309 sources
6310 }
6311
6312 #[cfg(feature = "discovery")]
6313 fn advertised_network_variables_for_link_locked(
6314 &self,
6315 st: &RouterInner,
6316 now_ms: u64,
6317 exclude_side: Option<RouterSideId>,
6318 ) -> Vec<DataType> {
6319 let mut types: Vec<DataType> = st
6320 .managed_variable_types
6321 .iter()
6322 .copied()
6323 .map(DataType)
6324 .collect();
6325 for (&route_side, route) in st.discovery_routes.iter() {
6326 if exclude_side == Some(route_side) {
6327 continue;
6328 }
6329 if now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
6330 types.extend(route.reachable_network_variables.iter().copied());
6331 }
6332 }
6333 crate::small_sort::sort(&mut types);
6334 types.dedup();
6335 types
6336 }
6337
6338 #[cfg(feature = "discovery")]
6339 fn preferred_discovery_master_present_locked<'a>(
6340 &self,
6341 st: &'a RouterInner,
6342 now_ms: u64,
6343 ) -> Option<&'a str> {
6344 if let Some(preferred) = st.preferred_discovery_master.as_deref()
6345 && (preferred == self.sender_arc().as_ref()
6346 || st
6347 .discovery_routes
6348 .values()
6349 .filter(|route| {
6350 now_ms.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6351 })
6352 .any(|route| {
6353 route
6354 .announcers
6355 .iter()
6356 .filter(|(_, peer)| {
6357 now_ms.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6358 })
6359 .any(|(name, peer)| {
6360 name == preferred
6361 || peer.topology_boards.iter().any(|board| {
6362 board.sender_id == preferred
6363 || board
6364 .connections
6365 .iter()
6366 .any(|name| name == preferred)
6367 })
6368 })
6369 }))
6370 {
6371 return Some(preferred);
6374 }
6375 None
6376 }
6377
6378 #[cfg(feature = "discovery")]
6379 fn discovery_master_sender_locked(&self, st: &RouterInner, now_ms: u64) -> String {
6380 if let Some(preferred) = self.preferred_discovery_master_present_locked(st, now_ms) {
6381 return preferred.to_string();
6382 }
6383 let boards = self.advertised_discovery_topology_for_link_locked(st, now_ms, true, None);
6384 discovery::elect_discovery_master(self.sender_arc().as_ref(), &boards)
6385 }
6386
6387 #[cfg(all(feature = "discovery", feature = "timesync"))]
6388 #[inline]
6389 fn side_is_slow_control_link_locked(
6390 st: &RouterInner,
6391 side_id: RouterSideId,
6392 now_ms: u64,
6393 ) -> bool {
6394 st.adaptive_route_stats.get(&side_id).is_some_and(|stats| {
6395 let recent_slow = stats.last_slow_observed_ms > 0
6396 && now_ms.saturating_sub(stats.last_slow_observed_ms)
6397 <= discovery::DISCOVERY_SLOW_LINK_FULL_INTERVAL_MS;
6398 stats.sample_count > 0
6399 && ((stats.estimated_bandwidth_bps > 0
6400 && stats.estimated_bandwidth_bps <= CONTROL_SLOW_LINK_CAPACITY_BPS)
6401 || recent_slow)
6402 })
6403 }
6404
6405 #[cfg(feature = "discovery")]
6406 fn discovery_level_for_side_locked(
6407 st: &mut RouterInner,
6408 side_id: RouterSideId,
6409 now_ms: u64,
6410 ) -> Option<DiscoveryAdvertiseLevel> {
6411 let throttle = st.discovery_side_throttle.entry(side_id).or_default();
6412 if !throttle.has_sent_full {
6413 throttle.has_sent_full = true;
6414 throttle.pending_incremental = false;
6415 throttle.incremental_retries_remaining = 0;
6416 throttle.next_ping_ms = now_ms.saturating_add(DISCOVERY_SLOW_LINK_PING_INTERVAL_MS);
6417 return Some(DiscoveryAdvertiseLevel::Full);
6418 }
6419 if throttle.pending_incremental {
6420 throttle.incremental_retries_remaining =
6421 throttle.incremental_retries_remaining.saturating_sub(1);
6422 throttle.pending_incremental = throttle.incremental_retries_remaining > 0;
6423 return Some(DiscoveryAdvertiseLevel::Incremental);
6424 }
6425 if throttle.liveness_due(now_ms) {
6428 throttle.next_ping_ms = now_ms.saturating_add(DISCOVERY_SLOW_LINK_PING_INTERVAL_MS);
6429 return Some(DiscoveryAdvertiseLevel::MinimalPing);
6430 }
6431 None
6432 }
6433
6434 #[cfg(all(feature = "discovery", feature = "timesync"))]
6435 #[inline]
6436 fn is_timesync_type(ty: DataType) -> bool {
6437 matches!(
6438 ty,
6439 DataType::TimeSyncAnnounce | DataType::TimeSyncRequest | DataType::TimeSyncResponse
6440 )
6441 }
6442
6443 #[cfg(all(feature = "discovery", feature = "timesync"))]
6444 fn timesync_allowed_for_side_locked(
6445 st: &mut RouterInner,
6446 side_id: RouterSideId,
6447 ty: DataType,
6448 now_ms: u64,
6449 ) -> bool {
6450 if !Self::is_timesync_type(ty) {
6451 return true;
6452 }
6453 if !Self::side_is_slow_control_link_locked(st, side_id, now_ms) {
6454 st.timesync_side_throttle.remove(&side_id);
6455 return true;
6456 }
6457
6458 let throttle = st.timesync_side_throttle.entry(side_id).or_default();
6459 if now_ms >= throttle.next_allowed_ms {
6460 throttle.next_allowed_ms = now_ms.saturating_add(TIMESYNC_SLOW_LINK_MIN_INTERVAL_MS);
6461 return true;
6462 }
6463 false
6464 }
6465
6466 #[cfg(all(feature = "discovery", feature = "timesync"))]
6467 fn filter_timesync_sides_locked(
6468 st: &mut RouterInner,
6469 ty: DataType,
6470 now_ms: u64,
6471 sides: Vec<RouterSideId>,
6472 ) -> Vec<RouterSideId> {
6473 sides
6474 .into_iter()
6475 .filter(|side| Self::timesync_allowed_for_side_locked(st, *side, ty, now_ms))
6476 .collect()
6477 }
6478
6479 #[cfg(all(feature = "discovery", feature = "timesync"))]
6480 fn filter_timesync_matches_locked(
6481 st: &mut RouterInner,
6482 ty: DataType,
6483 now_ms: u64,
6484 matches: Vec<DiscoveryCandidateMatch>,
6485 ) -> Vec<DiscoveryCandidateMatch> {
6486 matches
6487 .into_iter()
6488 .filter(|m| Self::timesync_allowed_for_side_locked(st, m.side, ty, now_ms))
6489 .collect()
6490 }
6491
6492 #[cfg(feature = "discovery")]
6493 fn refresh_known_discovery_liveness(
6494 &self,
6495 pkt: &Packet,
6496 side: RouterSideId,
6497 ) -> TelemetryResult<bool> {
6498 let empty_ping =
6499 pkt.data_type() == crate::DataType::DiscoveryAnnounce && pkt.payload().is_empty();
6500 if !empty_ping && pkt.data_type() != crate::DataType::DiscoveryAddress {
6501 return Ok(false);
6502 }
6503 crate::memory_admission::check(
6504 if empty_ping {
6505 512
6506 } else {
6507 1024 + pkt.payload().len().saturating_mul(4)
6508 },
6509 pkt.payload().len().saturating_mul(2).max(128),
6510 )?;
6511 let mut address = if empty_ping {
6512 None
6513 } else {
6514 Some(discovery::decode_discovery_address(pkt)?)
6515 };
6516 let now = self.clock.now_ms();
6517 let mut st = self.state.lock();
6518 if let Some(ad) = &mut address {
6519 let link_local = st
6520 .sides
6521 .get(side)
6522 .and_then(|entry| entry.as_ref())
6523 .is_some_and(|entry| entry.opts.link_local_enabled);
6524 if !link_local {
6525 ad.reachable_endpoints.retain(|ep| !ep.is_link_local_only());
6526 }
6527 let Some(entry) = st.address_book.get(ad.hostname.as_str()) else {
6528 return Ok(false);
6529 };
6530 if entry.address != ad.address
6531 || entry.requested_address != ad.requested_address
6532 || entry.mode != Self::address_mode_from_code(ad.mode, ad.requested_address)
6533 || entry.owner_hash != ad.owner_hash
6534 {
6535 return Ok(false);
6536 }
6537 if entry.birth_ms != ad.birth_ms
6538 && st.address_book.iter().any(|(hostname, other)| {
6539 hostname != &ad.hostname
6540 && [ad.address, ad.requested_address].iter().any(|candidate| {
6541 *candidate != 0
6542 && (other.address == *candidate
6543 || other.requested_address == *candidate)
6544 })
6545 })
6546 {
6547 return Ok(false);
6551 }
6552 }
6553 let canonical;
6554 let sender = if let Some(ad) = &address {
6555 ad.hostname.as_str()
6556 } else {
6557 canonical = Self::canonical_sender_locked(&st, pkt.sender());
6558 canonical.as_str()
6559 };
6560 let Some(route) = st.discovery_routes.get_mut(&side) else {
6561 return Ok(false);
6562 };
6563 let Some(peer) = route.announcers.get_mut(sender) else {
6564 return Ok(false);
6565 };
6566 if let Some(ad) = &address
6567 && (peer.advertised_address != Some(ad.address)
6568 || peer.advertised_reachable != ad.reachable_endpoints
6569 || peer.advertised_reachable_timesync_sources != ad.reachable_timesync_sources
6570 || peer.reachable_network_variables != ad.reachable_network_variables
6571 || peer.link_capabilities != Some(ad.link_capabilities))
6572 {
6573 return Ok(false);
6574 }
6575 peer.last_seen_ms = now;
6578 route.last_seen_ms = route.last_seen_ms.max(now);
6579 if let Some(ad) = &address
6580 && let Some(entry) = st.address_book.get_mut(ad.hostname.as_str())
6581 {
6582 entry.birth_ms = ad.birth_ms;
6585 entry.last_seen_ms = now;
6586 }
6587 Ok(true)
6588 }
6589
6590 #[cfg(feature = "discovery")]
6591 fn queue_pressure_keepalives(
6592 &self,
6593 requested_side: Option<RouterSideId>,
6594 ) -> TelemetryResult<()> {
6595 let now = self.clock.now_ms();
6596 let sender = self.sender_arc();
6597 crate::memory_admission::check(
6598 256 + sender.len().saturating_mul(4),
6599 sender.len().max(256),
6600 )?;
6601 let side_count = self.state.lock().sides.len();
6602 for side in 0..side_count {
6603 let due = {
6604 let st = self.state.lock();
6605 requested_side.is_none_or(|wanted| wanted == side)
6606 && st.sides[side].is_some()
6607 && self.route_allowed_locked(
6608 &st,
6609 None,
6610 Some(crate::DataType::DiscoveryAnnounce),
6611 side,
6612 )
6613 && st
6614 .discovery_side_throttle
6615 .get(&side)
6616 .is_none_or(|throttle| now >= throttle.next_ping_ms)
6617 };
6618 if !due {
6619 continue;
6620 }
6621 let summary = {
6624 let st = self.state.lock();
6625 let extra = st
6626 .discovery_routes
6627 .iter()
6628 .filter(|(id, _)| **id != side)
6629 .fold(0usize, |total, (_, route)| {
6630 total
6631 .saturating_add(route.reachable.len().saturating_mul(32))
6632 .saturating_add(
6633 route.reachable_network_variables.len().saturating_mul(32),
6634 )
6635 .saturating_add(
6636 route
6637 .reachable_timesync_sources
6638 .iter()
6639 .map(|source| source.len().saturating_add(32).saturating_mul(4))
6640 .sum::<usize>(),
6641 )
6642 });
6643 if crate::memory_admission::check(4096usize.saturating_add(extra), 2048).is_ok() {
6644 let opts = st.sides[side].as_ref().expect("checked above").opts.clone();
6645 let mut endpoints = self.local_discovery_endpoints(&st);
6646 let mut variables =
6647 self.advertised_network_variables_for_link_locked(&st, now, Some(side));
6648 let mut sources = self.local_discovery_timesync_sources(now);
6649 for (&route_side, route) in &st.discovery_routes {
6650 if route_side == side {
6651 continue;
6652 }
6653 for peer in route.announcers.values() {
6654 if now.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS {
6655 endpoints.extend(peer.reachable.iter().copied());
6656 sources.extend(peer.reachable_timesync_sources.iter().cloned());
6657 }
6658 }
6659 }
6660 endpoints.retain(|ep| {
6661 !discovery::is_discovery_endpoint(*ep)
6662 && (opts.link_local_enabled || !ep.is_link_local_only())
6663 });
6664 crate::small_sort::sort(&mut endpoints);
6665 endpoints.dedup();
6666 crate::small_sort::sort(&mut variables);
6667 variables.dedup();
6668 sources.sort_unstable();
6669 sources.dedup();
6670 Some((endpoints, variables, sources, opts.link_capabilities()))
6671 } else {
6672 None
6673 }
6674 };
6675 let pkt = if let Some((endpoints, variables, sources, capabilities)) = summary {
6676 let address = self.local_address_advertisement(
6677 endpoints,
6678 variables,
6679 sources,
6680 capabilities,
6681 discovery::ADDRESS_STATE_REQUEST,
6682 );
6683 discovery::build_discovery_address(sender.as_ref(), now, &address)?
6684 } else {
6685 discovery::build_discovery_announce(sender.as_ref(), now, &[])?
6686 };
6687 self.emit_internal_tx(
6688 RouterTxItem::ToSide {
6689 src: None,
6690 dst: side,
6691 data: RouterItem::Packet(pkt),
6692 },
6693 true,
6694 true,
6695 )?;
6696 self.state
6697 .lock()
6698 .discovery_side_throttle
6699 .entry(side)
6700 .or_default()
6701 .next_ping_ms = now.saturating_add(discovery::DISCOVERY_SLOW_INTERVAL_MS);
6702 }
6703 Ok(())
6704 }
6705
6706 #[cfg(feature = "discovery")]
6707 fn emit_discovery_snapshot(
6708 &self,
6709 called_from_queue: bool,
6710 include_schema: bool,
6711 include_topology: bool,
6712 skip_in_flight_sides: bool,
6713 ) -> TelemetryResult<()> {
6714 self.emit_discovery_snapshot_on_side(
6715 called_from_queue,
6716 include_schema,
6717 include_topology,
6718 skip_in_flight_sides,
6719 None,
6720 )
6721 }
6722
6723 #[cfg(feature = "discovery")]
6724 fn emit_discovery_snapshot_on_side(
6725 &self,
6726 called_from_queue: bool,
6727 include_schema: bool,
6728 include_topology: bool,
6729 skip_in_flight_sides: bool,
6730 requested_side: Option<RouterSideId>,
6731 ) -> TelemetryResult<()> {
6732 if requested_side.is_none() {
6733 let count = self.state.lock().sides.len();
6734 for side in 0..count {
6735 if self.state.lock().sides[side].is_some() {
6736 self.emit_discovery_snapshot_on_side(
6737 called_from_queue,
6738 include_schema,
6739 include_topology,
6740 skip_in_flight_sides,
6741 Some(side),
6742 )?;
6743 }
6744 }
6745 return Ok(());
6746 }
6747 let (scratch, largest) = if include_schema {
6748 (8192, 2048)
6749 } else {
6750 let now = self.clock.now_ms();
6751 let sender = self.sender_arc();
6752 let st = self.state.lock();
6753 let election_required = self
6754 .preferred_discovery_master_present_locked(&st, now)
6755 .is_none();
6756 let snapshot = discovery::topology_snapshot_scratch(
6757 sender.as_ref(),
6758 self.cfg.handlers.len(),
6759 st.managed_variable_types.len().saturating_add(
6760 st.discovery_routes
6761 .iter()
6762 .filter(|(side, _)| Some(**side) != requested_side)
6763 .map(|(_, r)| r.reachable_network_variables.len())
6764 .sum::<usize>(),
6765 ),
6766 4,
6767 true,
6768 st.discovery_routes
6769 .iter()
6770 .filter(|(side, route)| {
6771 (Some(**side) != requested_side)
6772 && now.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6773 })
6774 .flat_map(|(_, route)| route.announcers.iter())
6775 .filter(|(_, peer)| {
6776 now.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6777 })
6778 .map(|(name, peer)| {
6779 (
6780 name.as_str(),
6781 peer.reachable.as_slice(),
6782 peer.reachable_timesync_sources.as_slice(),
6783 peer.topology_boards.as_slice(),
6784 )
6785 }),
6786 );
6787 if !election_required {
6788 snapshot
6789 } else {
6790 let election = discovery::topology_snapshot_scratch(
6791 sender.as_ref(),
6792 self.cfg.handlers.len(),
6793 0,
6794 2,
6795 false,
6796 st.discovery_routes
6797 .values()
6798 .filter(|route| {
6799 now.saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6800 })
6801 .flat_map(|route| route.announcers.iter())
6802 .filter(|(_, peer)| {
6803 now.saturating_sub(peer.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
6804 })
6805 .map(|(name, peer)| {
6806 (
6807 name.as_str(),
6808 peer.reachable.as_slice(),
6809 peer.reachable_timesync_sources.as_slice(),
6810 peer.topology_boards.as_slice(),
6811 )
6812 }),
6813 );
6814 (snapshot.0.max(election.0), snapshot.1.max(election.1))
6815 }
6816 };
6817 while let Err(err) = crate::memory_admission::check(scratch, largest) {
6822 if matches!(err, TelemetryError::Io("memory pressure"))
6823 && crate::memory_admission::check(512, 256).is_ok()
6824 {
6825 let mut st = self.state.lock();
6826 if let Some(victim) = st.lower_priority_shared_queue(199)
6827 && st.pop_shared_queue_item(victim)
6828 {
6829 continue;
6830 }
6831 }
6832 if !include_schema && matches!(err, TelemetryError::Io("memory pressure")) {
6833 return self.queue_pressure_keepalives(requested_side);
6834 }
6835 return Err(err);
6836 }
6837 let now_ms = self.clock.now_ms();
6838 let per_side = {
6839 let mut st = self.state.lock();
6840 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
6841 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
6842 Self::note_discovery_topology_change_locked(&mut st, now_ms);
6843 }
6844 st.fit_discovery_budget();
6845 let local_is_master =
6846 self.discovery_master_sender_locked(&st, now_ms) == self.sender_arc().as_ref();
6847 let mut per_side = Vec::new();
6848 for side_id in 0..st.sides.len() {
6849 let Some(side) = st.sides[side_id].as_ref() else {
6850 continue;
6851 };
6852 let opts = side.opts.clone();
6853 let link_local_enabled = opts.link_local_enabled;
6854 if requested_side.is_some_and(|requested| requested != side_id) {
6855 continue;
6856 }
6857 let discovery_in_flight = skip_in_flight_sides
6858 && st.reliable_tx.iter().any(|((pending_side, ty), tx_state)| {
6859 *pending_side == side_id
6860 && !tx_state.sent.is_empty()
6861 && DataType::try_from_u32(*ty).is_some_and(discovery::is_discovery_type)
6862 });
6863 if !self.route_allowed_locked(&st, None, Some(DataType::DiscoveryAnnounce), side_id)
6864 {
6865 continue;
6866 }
6867 let level = if discovery_in_flight {
6871 let throttle = st.discovery_side_throttle.entry(side_id).or_default();
6872 if !throttle.liveness_due(now_ms) {
6873 None
6874 } else {
6875 throttle.next_ping_ms =
6876 now_ms.saturating_add(DISCOVERY_SLOW_LINK_PING_INTERVAL_MS);
6877 Some(DiscoveryAdvertiseLevel::MinimalPing)
6878 }
6879 } else {
6880 Self::discovery_level_for_side_locked(&mut st, side_id, now_ms)
6881 };
6882 let Some(level) = level else {
6883 continue;
6884 };
6885 let include_side_topology = include_topology
6886 && self.route_allowed_locked(
6887 &st,
6888 None,
6889 Some(DataType::DiscoveryTopology),
6890 side_id,
6891 );
6892 let capabilities = opts.link_capabilities();
6893 if level == DiscoveryAdvertiseLevel::MinimalPing {
6894 per_side.push((
6895 side_id,
6896 level,
6897 Vec::new(),
6898 Vec::new(),
6899 Vec::new(),
6900 Vec::new(),
6901 Vec::new(),
6902 capabilities,
6903 local_is_master,
6904 ));
6905 continue;
6906 }
6907 let current_topology = if include_side_topology {
6908 self.advertised_discovery_topology_for_link_locked(
6909 &st,
6910 now_ms,
6911 link_local_enabled,
6912 Some(side_id),
6913 )
6914 } else {
6915 Vec::new()
6916 };
6917 let (endpoints, timesync_sources) = if include_side_topology {
6918 self.advertised_summaries_from_topology_locked(
6919 &st,
6920 now_ms,
6921 link_local_enabled,
6922 Some(side_id),
6923 ¤t_topology,
6924 )
6925 } else {
6926 (
6927 self.advertised_discovery_endpoints_for_link_locked(
6928 &st,
6929 now_ms,
6930 link_local_enabled,
6931 Some(side_id),
6932 ),
6933 self.advertised_discovery_timesync_sources_for_link_locked(
6934 &st,
6935 now_ms,
6936 Some(side_id),
6937 ),
6938 )
6939 };
6940 let (topology, removed_topology) = match level {
6941 DiscoveryAdvertiseLevel::Full => (current_topology.clone(), Vec::new()),
6942 DiscoveryAdvertiseLevel::Incremental => {
6943 let previous = &st
6944 .discovery_side_throttle
6945 .get(&side_id)
6946 .expect("discovery throttle exists")
6947 .last_topology;
6948 discovery::topology_delta(previous, ¤t_topology)
6949 }
6950 DiscoveryAdvertiseLevel::MinimalPing => (Vec::new(), Vec::new()),
6951 };
6952 let throttle = st
6953 .discovery_side_throttle
6954 .get_mut(&side_id)
6955 .expect("discovery throttle exists");
6956 if level == DiscoveryAdvertiseLevel::Full
6957 || (level == DiscoveryAdvertiseLevel::Incremental
6958 && throttle.incremental_retries_remaining == 0)
6959 {
6960 throttle.last_topology = current_topology;
6961 }
6962 per_side.push((
6963 side_id,
6964 level,
6965 endpoints,
6966 self.advertised_network_variables_for_link_locked(&st, now_ms, Some(side_id)),
6967 timesync_sources,
6968 topology,
6969 removed_topology,
6970 capabilities,
6971 local_is_master,
6972 ));
6973 }
6974 per_side
6975 };
6976 for (
6977 side_id,
6978 level,
6979 endpoints,
6980 network_variables,
6981 timesync_sources,
6982 topology,
6983 removed_topology,
6984 capabilities,
6985 local_is_master,
6986 ) in per_side
6987 {
6988 let sender = self.sender_arc();
6989 if matches!(
6990 level,
6991 DiscoveryAdvertiseLevel::Full | DiscoveryAdvertiseLevel::Incremental
6992 ) {
6993 let address = self.local_address_advertisement(
6994 endpoints.clone(),
6995 network_variables.clone(),
6996 timesync_sources.clone(),
6997 capabilities,
6998 if local_is_master {
6999 discovery::ADDRESS_STATE_APPROVED
7000 } else {
7001 discovery::ADDRESS_STATE_REQUEST
7002 },
7003 );
7004 let pkt = discovery::build_discovery_address(sender.as_ref(), now_ms, &address)?;
7005 self.emit_internal_tx(
7006 RouterTxItem::ToSide {
7007 src: None,
7008 dst: side_id,
7009 data: RouterItem::Packet(pkt),
7010 },
7011 true,
7012 called_from_queue,
7013 )?;
7014 }
7015 if level == DiscoveryAdvertiseLevel::MinimalPing {
7016 let pkt = discovery::build_discovery_announce(
7017 sender.as_ref(),
7018 now_ms,
7019 endpoints.as_slice(),
7020 )?;
7021 self.emit_internal_tx(
7022 RouterTxItem::ToSide {
7023 src: None,
7024 dst: side_id,
7025 data: RouterItem::Packet(pkt),
7026 },
7027 true,
7028 called_from_queue,
7029 )?;
7030 }
7031 if include_topology
7032 && (!topology.is_empty() || !removed_topology.is_empty())
7033 && level != DiscoveryAdvertiseLevel::MinimalPing
7034 {
7035 let pkt = if level == DiscoveryAdvertiseLevel::Full {
7036 discovery::build_discovery_topology(sender.as_ref(), now_ms, &topology)?
7037 } else {
7038 discovery::build_discovery_topology_delta(
7039 sender.as_ref(),
7040 now_ms,
7041 &topology,
7042 &removed_topology,
7043 )?
7044 };
7045 self.emit_internal_tx(
7046 RouterTxItem::ToSide {
7047 src: None,
7048 dst: side_id,
7049 data: RouterItem::Packet(pkt),
7050 },
7051 true,
7052 called_from_queue,
7053 )?;
7054 }
7055 drop((
7058 endpoints,
7059 network_variables,
7060 timesync_sources,
7061 topology,
7062 removed_topology,
7063 ));
7064 if include_schema && level == DiscoveryAdvertiseLevel::Full {
7069 let pkt = discovery::build_discovery_schema(sender.as_ref(), now_ms)?;
7070 self.emit_internal_tx(
7071 RouterTxItem::ToSide {
7072 src: None,
7073 dst: side_id,
7074 data: RouterItem::Packet(pkt),
7075 },
7076 true,
7077 called_from_queue,
7078 )?;
7079 }
7080 }
7081 Ok(())
7082 }
7083
7084 #[cfg(feature = "discovery")]
7085 fn queue_discovery_announce(
7086 &self,
7087 include_schema: bool,
7088 skip_in_flight_sides: bool,
7089 ) -> TelemetryResult<()> {
7090 let now_ms = self.clock.now_ms();
7091 {
7092 let mut st = self.state.lock();
7093 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
7094 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7095 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7096 }
7097 st.fit_discovery_budget();
7098 if st.sides.iter().all(|side| side.is_none()) {
7099 return Ok(());
7100 }
7101 if include_schema {
7104 for throttle in st.discovery_side_throttle.values_mut() {
7105 throttle.has_sent_full = false;
7106 }
7107 }
7108 st.discovery_cadence.on_announce_sent(now_ms);
7109 }
7110 self.emit_discovery_snapshot(true, include_schema, true, skip_in_flight_sides)
7111 }
7112
7113 #[cfg(feature = "discovery")]
7114 fn note_schema_received(&self, side: RouterSideId, sender: &str) {
7115 let mut st = self.state.lock();
7116 let address = sender
7120 .strip_prefix("@addr:")
7121 .and_then(|value| value.parse::<u32>().ok());
7122 if let Some(route) = st.discovery_routes.get_mut(&side) {
7123 for (name, peer) in &mut route.announcers {
7124 if name == sender
7125 || address.is_some_and(|address| {
7126 peer.advertised_address == Some(address)
7127 || sender_address_u32(name) == address
7128 })
7129 {
7130 peer.has_schema = true;
7131 break;
7132 }
7133 }
7134 }
7135 }
7136
7137 #[cfg(feature = "discovery")]
7138 fn poll_discovery_announce(&self) -> TelemetryResult<bool> {
7139 let now_ms = self.clock.now_ms();
7140 let recovery_sides = {
7144 let mut st = self.state.lock();
7145 let mut due = Vec::new();
7146 for side in 0..st.sides.len() {
7147 let missing = st.discovery_routes.get(&side).is_some_and(|route| {
7148 route.announcers.iter().any(|(name, peer)| {
7149 !peer.has_full_topology
7150 && now_ms.saturating_sub(peer.last_seen_ms) < DISCOVERY_ROUTE_TTL_MS
7151 && !route.announcers.values().any(|bridge| {
7152 bridge.has_full_topology
7153 && now_ms.saturating_sub(bridge.last_seen_ms)
7154 < DISCOVERY_ROUTE_TTL_MS
7155 && bridge
7156 .topology_boards
7157 .iter()
7158 .any(|board| board.sender_id == *name)
7159 })
7160 })
7161 });
7162 if !missing {
7163 continue;
7164 }
7165 let throttle = st.discovery_side_throttle.entry(side).or_default();
7166 if throttle.next_topology_request_ms == 0 {
7167 throttle.next_topology_request_ms =
7168 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_GRACE_MS);
7169 } else if now_ms >= throttle.next_topology_request_ms {
7170 throttle.next_topology_request_ms =
7171 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_RETRY_MS);
7172 due.push(side);
7173 }
7174 }
7175 due
7176 };
7177 let recovering = !recovery_sides.is_empty();
7178 for side in recovery_sides {
7179 let pkt =
7180 discovery::build_discovery_topology_request(self.sender_arc().as_ref(), now_ms)?;
7181 self.emit_internal_tx(
7182 RouterTxItem::ToSide {
7183 src: None,
7184 dst: side,
7185 data: RouterItem::Packet(pkt),
7186 },
7187 true,
7188 true,
7189 )?;
7190 }
7191 let schema_recovery_sides = {
7192 let mut st = self.state.lock();
7193 let mut due = Vec::new();
7194 for side in 0..st.sides.len() {
7195 let missing = st.discovery_routes.get(&side).is_some_and(|route| {
7196 route.announcers.values().any(|peer| {
7197 peer.has_full_topology
7198 && !peer.has_schema
7199 && now_ms.saturating_sub(peer.last_seen_ms) < DISCOVERY_ROUTE_TTL_MS
7200 })
7201 });
7202 if !missing {
7203 continue;
7204 }
7205 let throttle = st.discovery_side_throttle.entry(side).or_default();
7206 if throttle.next_schema_request_ms == 0 {
7207 throttle.next_schema_request_ms =
7208 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_GRACE_MS);
7209 } else if now_ms >= throttle.next_schema_request_ms {
7210 throttle.next_schema_request_ms =
7211 now_ms.saturating_add(discovery::DISCOVERY_BASELINE_RETRY_MS);
7212 due.push(side);
7213 }
7214 }
7215 due
7216 };
7217 let recovering = recovering || !schema_recovery_sides.is_empty();
7218 for side in schema_recovery_sides {
7219 let pkt =
7220 discovery::build_discovery_schema_request(self.sender_arc().as_ref(), now_ms)?;
7221 self.emit_internal_tx(
7222 RouterTxItem::ToSide {
7223 src: None,
7224 dst: side,
7225 data: RouterItem::Packet(pkt),
7226 },
7227 true,
7228 true,
7229 )?;
7230 }
7231 let due = {
7232 let mut st = self.state.lock();
7233 let removed = Self::prune_discovery_routes_locked(&mut st, now_ms);
7234 if removed {
7235 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7236 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7237 }
7238 st.fit_discovery_budget();
7239 let has_any = st.sides.iter().enumerate().any(|(side_id, side)| {
7240 let Some(side) = side.as_ref() else {
7241 return false;
7242 };
7243 if !self.route_allowed_locked(&st, None, Some(DataType::DiscoveryAnnounce), side_id)
7244 {
7245 return false;
7246 }
7247 let _ = side;
7248 let in_flight = st.reliable_tx.iter().any(|((pending_side, ty), tx_state)| {
7249 *pending_side == side_id
7250 && !tx_state.sent.is_empty()
7251 && DataType::try_from_u32(*ty).is_some_and(discovery::is_discovery_type)
7252 });
7253 st.discovery_side_throttle
7254 .get(&side_id)
7255 .is_none_or(|throttle| {
7256 (!in_flight && (!throttle.has_sent_full || throttle.pending_incremental))
7257 || throttle.liveness_due(now_ms)
7258 })
7259 });
7260 if st.sides.is_empty() || !has_any {
7261 return Ok(recovering);
7262 }
7263 st.discovery_cadence.due(now_ms)
7264 };
7265 if !due {
7266 return Ok(recovering);
7267 }
7268 self.queue_discovery_announce(false, true)?;
7277 Ok(true)
7278 }
7279
7280 #[cfg(feature = "discovery")]
7281 fn learn_discovery_packet(
7282 &self,
7283 pkt: &Packet,
7284 src: Option<RouterSideId>,
7285 called_from_queue: bool,
7286 ) -> TelemetryResult<bool> {
7287 if !discovery::is_discovery_type(pkt.data_type()) {
7288 return Ok(false);
7289 }
7290 let Some(side) = src else {
7291 return Ok(true);
7292 };
7293 if self.refresh_known_discovery_liveness(pkt, side)? {
7294 return Ok(true);
7295 }
7296 #[cfg(not(feature = "std"))]
7297 if pkt.data_type() == DataType::DiscoverySchema
7298 && discovery::discovery_schema_payload_is_fully_known(pkt.payload())?
7299 {
7300 self.note_schema_received(side, pkt.sender());
7301 return Ok(true);
7302 }
7303 if matches!(
7307 pkt.data_type(),
7308 DataType::DiscoveryTopologyRequest | DataType::DiscoverySchemaRequest
7309 ) {
7310 crate::memory_admission::check(1024, 256)?;
7311 if let Some(transport) = self.state.lock().side_transport.get_mut(&side) {
7316 transport.refresh_transmit_header_templates();
7317 }
7318 }
7319 if matches!(
7320 pkt.data_type(),
7321 DataType::DiscoveryTopologyRequest | DataType::DiscoverySchemaRequest
7322 ) {
7323 let now_ms = self.clock.now_ms();
7324 {
7325 let mut st = self.state.lock();
7326 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
7327 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7328 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7329 }
7330 let throttle = st.discovery_side_throttle.entry(side).or_default();
7331 throttle.has_sent_full = false;
7332 throttle.last_topology = Vec::new();
7336 }
7337 self.emit_discovery_snapshot_on_side(
7342 called_from_queue,
7343 pkt.data_type() == DataType::DiscoverySchemaRequest,
7344 true,
7345 false,
7346 Some(side),
7347 )?;
7348 return Ok(true);
7349 }
7350 let retained_summary = if matches!(
7351 pkt.data_type(),
7352 DataType::DiscoveryAddress
7353 | DataType::DiscoveryTopology
7354 | DataType::DiscoveryAnnounce
7355 | DataType::DiscoveryTimeSyncSources
7356 | DataType::ManagedVariableRequest
7357 ) {
7358 self.state
7359 .lock()
7360 .discovery_routes
7361 .get(&side)
7362 .map_or(0, |route| {
7363 RouterInner::discovery_route_byte_cost(side, route)
7364 })
7365 } else {
7366 0
7367 };
7368 let (decode_bytes, largest_block) = if pkt.data_type() == DataType::ManagedVariableRequest {
7369 let ty = discovery::decode_managed_variable_request(pkt)?;
7370 let existing = self
7371 .state
7372 .lock()
7373 .discovery_routes
7374 .get(&side)
7375 .is_some_and(|route| {
7376 route
7377 .requested_network_variables
7378 .iter()
7379 .any(|(id, _)| *id == ty)
7380 });
7381 if existing {
7382 (1024, 256)
7383 } else {
7384 (
7387 2048usize.saturating_add(retained_summary.saturating_mul(2)),
7388 retained_summary
7389 .checked_next_power_of_two()
7390 .unwrap_or(usize::MAX)
7391 .max(1024),
7392 )
7393 }
7394 } else {
7395 let scratch = if pkt.data_type() == DataType::DiscoveryAddress {
7396 4096usize.saturating_add(retained_summary)
7397 } else {
7398 8192usize.saturating_add(retained_summary)
7399 };
7400 let decode_bytes = pkt
7401 .payload()
7402 .len()
7403 .saturating_mul(4)
7404 .saturating_add(scratch);
7405 let largest_block = pkt.payload().len().saturating_mul(2).max(2048);
7406 (decode_bytes, largest_block)
7407 };
7408 while let Err(err) = crate::memory_admission::check(decode_bytes, largest_block) {
7409 if !matches!(err, TelemetryError::Io("memory pressure")) {
7410 return Err(err);
7411 }
7412 let mut st = self.state.lock();
7413 let Some(victim) = st.lower_priority_shared_queue(255) else {
7414 return Err(err);
7415 };
7416 if !st.pop_shared_queue_item(victim) {
7417 return Err(err);
7418 }
7419 }
7420 if pkt.data_type() == DataType::DiscoveryAddress {
7421 let mut ad = discovery::decode_discovery_address(pkt)?;
7422 let identity = ad.clone();
7423 let mut changed = false;
7424 let sender_id = if ad.hostname.is_empty() {
7430 pkt.sender()
7431 } else {
7432 ad.hostname.as_str()
7433 };
7434 let now_ms = self.clock.now_ms();
7435 let mut st = self.state.lock();
7436 let side_link_local_enabled = st
7437 .sides
7438 .get(side)
7439 .and_then(|entry| entry.as_ref())
7440 .map(|side_ref| side_ref.opts.link_local_enabled)
7441 .unwrap_or(false);
7442 if !side_link_local_enabled {
7443 ad.reachable_endpoints.retain(|ep| !ep.is_link_local_only());
7444 }
7445 let mut route = st.discovery_routes.get(&side).cloned().unwrap_or_default();
7446 let previously_reachable_network_variables = route.reachable_network_variables.clone();
7447 if pkt.sender() != sender_id {
7448 route.announcers.remove(pkt.sender());
7449 }
7450 let mut sender_state =
7451 core::mem::take(route.announcers.entry(sender_id.to_string()).or_default());
7452 if sender_state.advertised_address != Some(ad.address) {
7458 sender_state.advertised_address = Some(ad.address);
7459 changed = true;
7460 }
7461 if sender_state.advertised_reachable != ad.reachable_endpoints {
7462 sender_state.advertised_reachable = ad.reachable_endpoints;
7463 changed = true;
7464 }
7465 if sender_state.advertised_reachable_timesync_sources != ad.reachable_timesync_sources {
7466 sender_state.advertised_reachable_timesync_sources = ad.reachable_timesync_sources;
7467 changed = true;
7468 }
7469 if sender_state.reachable_network_variables != ad.reachable_network_variables {
7470 sender_state.reachable_network_variables = ad.reachable_network_variables;
7471 changed = true;
7472 }
7473 if sender_state.link_capabilities != Some(ad.link_capabilities) {
7474 sender_state.link_capabilities = Some(ad.link_capabilities);
7475 changed = true;
7476 }
7477 Self::refresh_sender_topology_state(&mut sender_state);
7478 sender_state.last_seen_ms = now_ms;
7479 route.announcers.insert(sender_id.to_string(), sender_state);
7480 Self::recompute_discovery_side_state(&mut route);
7481 let newly_reachable_network_variables: Vec<DataType> = route
7482 .reachable_network_variables
7483 .iter()
7484 .copied()
7485 .filter(|ty| !previously_reachable_network_variables.contains(ty))
7486 .collect();
7487 st.commit_discovery_route(side, route)?;
7488 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7489 if changed {
7490 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
7491 }
7492 let replay: Vec<Packet> = newly_reachable_network_variables
7498 .iter()
7499 .filter(|ty| Self::managed_variable_permissions_locked(&st, **ty).write)
7500 .filter_map(|ty| {
7501 st.managed_variable_latest
7502 .get(&ty.as_u32())
7503 .map(|entry| entry.packet.clone())
7504 })
7505 .collect();
7506 drop(st);
7507 self.ingest_address_advertisement(identity)?;
7508 for value in replay {
7509 self.emit_internal_tx_with_priority(
7510 RouterTxItem::ToSide {
7511 src: None,
7512 dst: side,
7513 data: RouterItem::Packet(value),
7514 },
7515 true,
7516 crate::transport_priority(DataType::ManagedVariableValue),
7517 called_from_queue,
7518 )?;
7519 }
7520 return Ok(true);
7521 }
7522 let mut decoded_topology = if pkt.data_type() == DataType::DiscoveryTopology {
7523 Some(discovery::decode_discovery_topology_update(pkt)?)
7524 } else {
7525 None
7526 };
7527 let packet_sender = {
7528 let st = self.state.lock();
7529 let canonical = Self::canonical_sender_locked(&st, pkt.sender());
7530 if canonical != pkt.sender() {
7531 canonical
7532 } else if let Some(address) = pkt
7533 .sender()
7534 .strip_prefix("@addr:")
7535 .and_then(|value| value.parse::<u32>().ok())
7536 {
7537 decoded_topology
7543 .as_ref()
7544 .and_then(|update| {
7545 update
7546 .boards
7547 .iter()
7548 .find(|board| sender_address_u32(&board.sender_id) == address)
7549 })
7550 .map(|board| board.sender_id.clone())
7551 .unwrap_or(canonical)
7552 } else {
7553 canonical
7554 }
7555 };
7556 let local_sender = self.sender_arc();
7557 if packet_sender == local_sender.as_ref() {
7558 return Ok(true);
7559 }
7560
7561 if pkt.data_type() == DataType::ManagedVariableRequest {
7562 let ty = discovery::decode_managed_variable_request(pkt)?;
7563 {
7569 let now_ms = self.clock.now_ms();
7570 let mut st = self.state.lock();
7571 let existing = st.discovery_routes.get_mut(&side).and_then(|route| {
7575 let (_, last_seen_ms) = route
7576 .requested_network_variables
7577 .iter_mut()
7578 .find(|(requested, _)| *requested == ty)?;
7579 *last_seen_ms = now_ms;
7580 route.last_seen_ms = route.last_seen_ms.max(now_ms);
7581 Some(())
7582 });
7583 let newly_reachable = existing.is_none();
7584 if newly_reachable {
7585 let mut route = st.discovery_routes.get(&side).cloned().unwrap_or_default();
7586 route.requested_network_variables.push((ty, now_ms));
7587 Self::recompute_discovery_side_state(&mut route);
7588 st.commit_discovery_route(side, route)?;
7589 }
7590 if newly_reachable {
7591 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7592 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7593 }
7594 }
7595 if !self.can_write_managed_variable(ty) {
7596 return Ok(false);
7601 }
7602 if let Some(value) = self.managed_variable_latest(ty) {
7603 self.emit_internal_tx(
7604 RouterTxItem::ToSide {
7605 src: None,
7606 dst: side,
7607 data: RouterItem::Packet(value),
7608 },
7609 true,
7610 called_from_queue,
7611 )?;
7612 return Ok(true);
7613 }
7614 return Ok(false);
7617 }
7618 if pkt.data_type() == DataType::DiscoverySchema {
7619 let snapshot = discovery::decode_discovery_schema(pkt)?;
7620 let mut st = self.state.lock();
7629 let budget = st.schema_growth_budget();
7630 let report = crate::config::merge_owned_schema_snapshot_with_budget(snapshot, budget)?;
7631 if report.changed() {
7632 st.fit_discovery_budget();
7633 }
7634 drop(st);
7635 self.note_schema_received(side, &packet_sender);
7636 return Ok(true);
7637 }
7638 if pkt.data_type() == DataType::DiscoveryLinkCapabilities {
7639 let _ = discovery::decode_discovery_link_capabilities(pkt)?;
7640 return Ok(true);
7641 }
7642 let mut announce = if pkt.data_type() == DataType::DiscoveryAnnounce {
7643 Some(discovery::decode_discovery_announce(pkt)?)
7644 } else {
7645 None
7646 };
7647 let mut time_sources = if pkt.data_type() == DataType::DiscoveryTimeSyncSources {
7648 Some(discovery::decode_discovery_timesync_sources(pkt)?)
7649 } else {
7650 None
7651 };
7652 let mut st = self.state.lock();
7653 let now_ms = self.clock.now_ms();
7654 if pkt.data_type() == DataType::DiscoveryLeave {
7655 let leaving = packet_sender.as_str();
7656 let before = st.discovery_routes.clone();
7657 for route in st.discovery_routes.values_mut() {
7658 route.announcers.remove(leaving);
7659 for sender_state in route.announcers.values_mut() {
7660 sender_state
7661 .topology_boards
7662 .retain(|board| board.sender_id != leaving);
7663 for board in sender_state.topology_boards.iter_mut() {
7664 board.connections.retain(|peer| peer != leaving);
7665 }
7666 Self::refresh_sender_topology_state(sender_state);
7667 }
7668 Self::recompute_discovery_side_state(route);
7669 }
7670 st.discovery_routes
7671 .retain(|_, route| !route.announcers.is_empty());
7672 if st.discovery_routes != before {
7673 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7674 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7675 }
7676 return Ok(true);
7677 }
7678 if let Some(update) = decoded_topology.as_mut() {
7680 for board in update.boards.iter_mut() {
7681 board.sender_id = Self::canonical_sender_locked(&st, &board.sender_id);
7682 for peer in board.connections.iter_mut() {
7683 *peer = Self::canonical_sender_locked(&st, peer);
7684 }
7685 }
7686 }
7687 let mut route = st.discovery_routes.get(&side).cloned().unwrap_or_default();
7688 let alias_state = if packet_sender != pkt.sender() {
7689 route.announcers.remove(pkt.sender())
7690 } else {
7691 None
7692 };
7693 let side_link_local_enabled = st
7694 .sides
7695 .get(side)
7696 .and_then(|entry| entry.as_ref())
7697 .map(|side_ref| side_ref.opts.link_local_enabled)
7698 .unwrap_or(false);
7699 let mut sender_state = route
7700 .announcers
7701 .get_mut(&packet_sender)
7702 .map(core::mem::take)
7703 .or(alias_state)
7704 .unwrap_or_default();
7705 let changed = match pkt.data_type() {
7706 DataType::DiscoveryAnnounce => {
7707 let mut reachable = announce.take().expect("decoded above");
7708 if reachable.is_empty() {
7713 false
7714 } else {
7715 if !side_link_local_enabled {
7716 reachable.retain(|ep| !ep.is_link_local_only());
7717 }
7718 let board = Self::sender_topology_board_mut(&mut sender_state, &packet_sender);
7719 let changed = board.reachable_endpoints != reachable;
7720 board.reachable_endpoints = reachable;
7721 Self::refresh_sender_topology_state(&mut sender_state);
7722 changed
7723 }
7724 }
7725 DataType::DiscoveryTimeSyncSources => {
7726 let sources = time_sources.take().expect("decoded above");
7727 let board = Self::sender_topology_board_mut(&mut sender_state, &packet_sender);
7728 let changed = board.reachable_timesync_sources != sources;
7729 board.reachable_timesync_sources = sources;
7730 Self::refresh_sender_topology_state(&mut sender_state);
7731 changed
7732 }
7733 DataType::DiscoveryTopology => {
7734 let mut update = decoded_topology
7735 .take()
7736 .expect("topology packet was decoded before route selection");
7737 if !side_link_local_enabled {
7738 for board in update.boards.iter_mut() {
7739 board
7740 .reachable_endpoints
7741 .retain(|ep| !ep.is_link_local_only());
7742 }
7743 }
7744 if !update.incremental {
7745 sender_state.has_full_topology = true;
7746 }
7747 let changed =
7748 discovery::apply_topology_update(&mut sender_state.topology_boards, update);
7749 Self::refresh_sender_topology_state(&mut sender_state);
7750 changed
7751 }
7752 DataType::DiscoverySchema => false,
7753 _ => false,
7754 };
7755 sender_state.last_seen_ms = now_ms;
7756 route.announcers.insert(packet_sender, sender_state);
7757 Self::recompute_discovery_side_state(&mut route);
7758 st.commit_discovery_route(side, route)?;
7759 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
7760 if changed {
7761 Self::note_discovery_topology_change_locked(&mut st, now_ms);
7762 }
7763 Ok(true)
7764 }
7765
7766 #[cfg(not(feature = "discovery"))]
7767 fn queue_discovery_announce(&self) -> TelemetryResult<()> {
7768 Ok(())
7769 }
7770
7771 #[cfg(not(feature = "discovery"))]
7772 fn poll_discovery_announce(&self) -> TelemetryResult<bool> {
7773 Ok(false)
7774 }
7775
7776 #[cfg(not(feature = "discovery"))]
7777 fn learn_discovery_packet(
7778 &self,
7779 _pkt: &Packet,
7780 _src: Option<RouterSideId>,
7781 _called_from_queue: bool,
7782 ) -> TelemetryResult<bool> {
7783 Ok(false)
7784 }
7785
7786 #[inline]
7787 fn reliable_key(side: RouterSideId, ty: DataType) -> (RouterSideId, u32) {
7788 (side, ty.as_u32())
7789 }
7790
7791 fn reliable_tx_state_mut<'a>(
7792 &'a self,
7793 st: &'a mut RouterInner,
7794 side: RouterSideId,
7795 ty: DataType,
7796 ) -> &'a mut ReliableTxState {
7797 let key = Self::reliable_key(side, ty);
7798 st.reliable_tx
7799 .entry(key)
7800 .or_insert_with(|| ReliableTxState {
7801 next_seq: 1,
7802 sent_order: VecDeque::new(),
7803 sent: BTreeMap::new(),
7804 })
7805 }
7806
7807 fn reliable_rx_state_mut<'a>(
7808 &'a self,
7809 st: &'a mut RouterInner,
7810 side: RouterSideId,
7811 ty: DataType,
7812 ) -> &'a mut ReliableRxState {
7813 let key = Self::reliable_key(side, ty);
7814 st.reliable_rx
7815 .entry(key)
7816 .or_insert_with(|| ReliableRxState {
7817 expected_seq: 1,
7818 buffered: BTreeMap::new(),
7819 })
7820 }
7821
7822 fn reliable_control_packet(
7823 &self,
7824 control_ty: DataType,
7825 ty: DataType,
7826 seq: u32,
7827 ) -> TelemetryResult<Packet> {
7828 let sender = self.sender_arc();
7829 Packet::new(
7830 control_ty,
7831 message_meta(control_ty).endpoints_ref(),
7832 sender.as_ref(),
7833 self.packet_timestamp_ms(),
7834 encode_slice_le(&[ty.as_u32(), seq]),
7835 )
7836 }
7837
7838 fn queue_reliable_ack(
7839 &self,
7840 side: RouterSideId,
7841 ty: DataType,
7842 seq: u32,
7843 called_from_queue: bool,
7844 ) -> TelemetryResult<()> {
7845 let pkt = self.reliable_control_packet(DataType::ReliableAck, ty, seq)?;
7846 self.emit_internal_tx_with_priority(
7847 RouterTxItem::ToSide {
7848 src: None,
7849 dst: side,
7850 data: RouterItem::Packet(pkt),
7851 },
7852 true,
7853 message_priority(DataType::ReliableAck),
7854 called_from_queue,
7855 )
7856 }
7857
7858 fn queue_reliable_packet_request(
7859 &self,
7860 side: RouterSideId,
7861 ty: DataType,
7862 seq: u32,
7863 called_from_queue: bool,
7864 ) -> TelemetryResult<()> {
7865 let pkt = self.reliable_control_packet(DataType::ReliablePacketRequest, ty, seq)?;
7866 self.emit_internal_tx_with_priority(
7867 RouterTxItem::ToSide {
7868 src: None,
7869 dst: side,
7870 data: RouterItem::Packet(pkt),
7871 },
7872 true,
7873 message_priority(DataType::ReliablePacketRequest),
7874 called_from_queue,
7875 )
7876 }
7877
7878 fn queue_reliable_partial_ack(
7879 &self,
7880 side: RouterSideId,
7881 ty: DataType,
7882 seq: u32,
7883 called_from_queue: bool,
7884 ) -> TelemetryResult<()> {
7885 let pkt = self.reliable_control_packet(DataType::ReliablePartialAck, ty, seq)?;
7886 self.emit_internal_tx_with_priority(
7887 RouterTxItem::ToSide {
7888 src: None,
7889 dst: side,
7890 data: RouterItem::Packet(pkt),
7891 },
7892 true,
7893 message_priority(DataType::ReliablePartialAck),
7894 called_from_queue,
7895 )
7896 }
7897
7898 fn handle_reliable_ack(&self, side: RouterSideId, ty: DataType, ack: u32) {
7899 let mut st = self.state.lock();
7900 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7901 if matches!(reliable_mode(ty), crate::ReliableMode::Unordered) {
7902 tx_state.sent.remove(&ack);
7903 tx_state.sent_order.retain(|seq| *seq != ack);
7904 return;
7905 }
7906
7907 while let Some(seq) = tx_state.sent_order.front().copied() {
7908 if seq > ack {
7909 break;
7910 }
7911 tx_state.sent_order.pop_front();
7912 tx_state.sent.remove(&seq);
7913 }
7914 }
7915
7916 fn handle_reliable_partial_ack(&self, side: RouterSideId, ty: DataType, seq: u32) {
7917 let mut st = self.state.lock();
7918 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7919 if let Some(sent) = tx_state.sent.get_mut(&seq) {
7920 sent.partial_acked = true;
7921 }
7922 }
7923
7924 fn queue_reliable_retransmit(
7925 &self,
7926 side: RouterSideId,
7927 ty: DataType,
7928 seq: u32,
7929 called_from_queue: bool,
7930 ) -> TelemetryResult<()> {
7931 let mut queued = None;
7932 {
7933 let mut st = self.state.lock();
7934 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
7935 if let Some(sent) = tx_state.sent.get_mut(&seq)
7936 && !sent.queued
7937 {
7938 sent.queued = true;
7939 sent.partial_acked = false;
7940 queued = Some(sent.bytes.clone());
7941 }
7942 }
7943
7944 if let Some(bytes) = queued {
7945 if called_from_queue {
7946 self.tx_queue_item_with_priority(
7947 RouterTxItem::ReliableReplay { dst: side, bytes },
7948 true,
7949 Self::router_item_priority_bumped(ty),
7950 )?;
7951 } else {
7952 self.tx_item_impl(
7953 RouterTxItem::ReliableReplay { dst: side, bytes },
7954 true,
7955 false,
7956 )?;
7957 }
7958 }
7959
7960 Ok(())
7961 }
7962
7963 fn send_reliable_raw_to_side(
7964 &self,
7965 side: RouterSideId,
7966 bytes: crate::SharedBytes,
7967 relayed: bool,
7968 ) -> TelemetryResult<()> {
7969 let handler = {
7970 let st = self.state.lock();
7971 let side_ref = Self::side_ref(&st, side)?;
7972 if !side_ref.opts.egress_enabled {
7973 return Ok(());
7974 }
7975 (side_ref.tx_handler.clone(), side_ref.opts.clone())
7976 };
7977
7978 let (handler, opts) = handler;
7979
7980 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
7981 return Err(TelemetryError::Io("side tx busy"));
7982 };
7983 let started_ms = self.clock.now_ms();
7984 let (ty, priority) = Self::item_type_priority(&RouterItem::Packed(bytes.clone()))?;
7985 let priority = priority.max(self.effective_transport_priority(ty));
7986 let result = match handler {
7987 packed @ (RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)) => {
7988 let frames = self.encode_side_transport_frames(side, opts, bytes.clone())?;
7989 let mut attempts_total = 0usize;
7990 let mut sent_bytes = 0usize;
7991 self.send_side_frames_resumable(side, frames, |frame| {
7992 match self.retry_with_attempts(runtime_max_handler_retries(), || {
7993 Self::packed_handler_call(&packed, frame, priority)
7994 }) {
7995 Ok((_, attempts)) => {
7996 attempts_total = attempts_total.saturating_add(attempts);
7997 sent_bytes = sent_bytes.saturating_add(frame.len());
7998 }
7999 Err((err, attempts)) => {
8000 self.note_side_tx_failure(
8001 side,
8002 ty,
8003 attempts_total.saturating_add(attempts),
8004 );
8005 return Err(err);
8006 }
8007 }
8008
8009 Ok(())
8010 })?;
8011 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
8012 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8013 return Ok(());
8014 }
8015 RouterTxHandlerFn::Packet(f) => {
8016 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
8017 self.retry_with_attempts(runtime_max_handler_retries(), || f(&pkt))
8018 }
8019 };
8020 match result {
8021 Ok((_, attempts)) => {
8022 self.record_side_tx_sample(side, bytes.len(), started_ms, self.clock.now_ms());
8023 self.note_side_tx_success(side, ty, bytes.len(), relayed, attempts);
8024 Ok(())
8025 }
8026 Err((err, attempts)) => {
8027 self.note_side_tx_failure(side, ty, attempts);
8028 Err(err)
8029 }
8030 }
8031 }
8032
8033 #[allow(unused_mut)]
8034 fn send_reliable_to_side(
8035 &self,
8036 side: RouterSideId,
8037 data: RouterItem,
8038 relayed: bool,
8039 ) -> TelemetryResult<()> {
8040 let end_to_end_ordered = !self.item_target_senders(&data)?.is_empty()
8041 && matches!(
8042 reliable_mode(self.item_route_info(&data)?.1),
8043 crate::ReliableMode::Ordered
8044 );
8045 let (handler, opts, hop_reliable_enabled) = {
8046 let st = self.state.lock();
8047 let side_ref = Self::side_ref(&st, side)?;
8048 let opts = side_ref.opts.clone();
8049 let hop_reliable_enabled = opts.reliable_enabled
8050 && !end_to_end_ordered
8051 && self.cfg.reliable_enabled()
8052 && !self.side_has_multiple_announcers_locked(&st, side, self.clock.now_ms());
8053 (side_ref.tx_handler.clone(), opts, hop_reliable_enabled)
8054 };
8055
8056 if !matches!(
8057 &handler,
8058 RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)
8059 ) {
8060 return self.call_side_tx_handler(side, &handler, &data, relayed);
8061 }
8062
8063 if !hop_reliable_enabled {
8064 let mut adjusted_opts = opts;
8065 adjusted_opts.reliable_enabled = false;
8066 let preserve_end_to_end_ack = true;
8071 if let Some(adjusted) =
8072 self.adjust_reliable_for_side(adjusted_opts, data, preserve_end_to_end_ack)?
8073 {
8074 return self.call_side_tx_handler(side, &handler, &adjusted, relayed);
8075 }
8076 return Ok(());
8077 }
8078
8079 let ty = match &data {
8080 RouterItem::Packet(pkt) => pkt.data_type(),
8081 RouterItem::Packed(bytes) => {
8082 wire_format::peek_routing_frame_info(bytes.as_ref())?
8083 .envelope
8084 .ty
8085 }
8086 };
8087 let priority = self.item_service_priority(&data)?;
8088
8089 if !is_reliable_type(ty) {
8090 if let Some(adjusted) = self.adjust_reliable_for_side(opts, data, true)? {
8091 self.call_side_tx_handler(side, &handler, &adjusted, relayed)?;
8092 }
8093 return Ok(());
8094 }
8095
8096 let (seq, flags) = {
8097 let mut st = self.state.lock();
8098 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
8099 if tx_state.sent.len() >= runtime_reliable_max_pending() {
8100 return Err(TelemetryError::PacketTooLarge(
8101 "router reliable history full",
8102 ));
8103 }
8104 let seq = tx_state.next_seq;
8105 let next = tx_state.next_seq.wrapping_add(1);
8106 tx_state.next_seq = if next == 0 { 1 } else { next };
8107 let flags = match reliable_mode(ty) {
8108 crate::ReliableMode::Unordered => wire_format::RELIABLE_FLAG_UNORDERED,
8109 _ => 0,
8110 };
8111 (seq, flags)
8112 };
8113
8114 let mut bytes: crate::SharedBytes = match data {
8115 RouterItem::Packet(pkt) => self.pack_packet_for_router(
8116 &pkt,
8117 Some(wire_format::ReliableHeader { flags, seq, ack: 0 }),
8118 )?,
8119 RouterItem::Packed(bytes) => {
8120 #[cfg(feature = "cryptography")]
8121 if self.e2e_seal_config_for_type(ty).is_some() {
8122 self.prepare_packed_for_remote(
8123 bytes,
8124 Some(Some(wire_format::ReliableHeader { flags, seq, ack: 0 })),
8125 )?
8126 } else {
8127 let Some(rewritten) =
8128 wire_format::rewrite_reliable_header_owned(bytes.as_ref(), flags, seq, 0)?
8129 else {
8130 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
8131 return Err(TelemetryError::Io("side tx busy"));
8132 };
8133 let started_ms = self.clock.now_ms();
8134 let frames =
8135 self.encode_side_transport_frames(side, opts, bytes.clone())?;
8136 let mut attempts_total = 0usize;
8137 let mut sent_bytes = 0usize;
8138 self.send_side_frames_resumable(side, frames, |frame| {
8139 match self.retry_with_attempts(runtime_max_handler_retries(), || {
8140 Self::packed_handler_call(&handler, frame, priority)
8141 }) {
8142 Ok((_, attempts)) => {
8143 attempts_total = attempts_total.saturating_add(attempts);
8144 sent_bytes = sent_bytes.saturating_add(frame.len());
8145 }
8146 Err((err, attempts)) => {
8147 self.note_side_tx_failure(
8148 side,
8149 ty,
8150 attempts_total.saturating_add(attempts),
8151 );
8152 return Err(err);
8153 }
8154 }
8155
8156 Ok(())
8157 })?;
8158 self.record_side_tx_sample(
8159 side,
8160 sent_bytes,
8161 started_ms,
8162 self.clock.now_ms(),
8163 );
8164 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8165 return Ok(());
8166 };
8167 crate::shared_bytes::convert(rewritten)
8168 }
8169 #[cfg(not(feature = "cryptography"))]
8170 {
8171 let Some(rewritten) =
8172 wire_format::rewrite_reliable_header_owned(bytes.as_ref(), flags, seq, 0)?
8173 else {
8174 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
8175 return Err(TelemetryError::Io("side tx busy"));
8176 };
8177 let started_ms = self.clock.now_ms();
8178 let frames =
8179 self.encode_side_transport_frames(side, opts, bytes.clone())?;
8180 let mut attempts_total = 0usize;
8181 let mut sent_bytes = 0usize;
8182 self.send_side_frames_resumable(side, frames, |frame| {
8183 match self.retry_with_attempts(runtime_max_handler_retries(), || {
8184 Self::packed_handler_call(&handler, frame, priority)
8185 }) {
8186 Ok((_, attempts)) => {
8187 attempts_total = attempts_total.saturating_add(attempts);
8188 sent_bytes = sent_bytes.saturating_add(frame.len());
8189 }
8190 Err((err, attempts)) => {
8191 self.note_side_tx_failure(
8192 side,
8193 ty,
8194 attempts_total.saturating_add(attempts),
8195 );
8196 return Err(err);
8197 }
8198 }
8199
8200 Ok(())
8201 })?;
8202 self.record_side_tx_sample(
8203 side,
8204 sent_bytes,
8205 started_ms,
8206 self.clock.now_ms(),
8207 );
8208 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8209 return Ok(());
8210 };
8211 rewritten.into()
8212 }
8213 }
8214 };
8215
8216 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
8217 return Err(TelemetryError::Io("side tx busy"));
8218 };
8219 let started_ms = self.clock.now_ms();
8220 let frames = self.encode_side_transport_frames(side, opts, bytes.clone())?;
8221 let mut attempts_total = 0usize;
8222 let mut sent_bytes = 0usize;
8223 self.send_side_frames_resumable(side, frames, |frame| {
8224 match self.retry_with_attempts(runtime_max_handler_retries(), || {
8225 Self::packed_handler_call(&handler, frame, priority)
8226 }) {
8227 Ok((_, attempts)) => {
8228 attempts_total = attempts_total.saturating_add(attempts);
8229 sent_bytes = sent_bytes.saturating_add(frame.len());
8230 }
8231 Err((err, attempts)) => {
8232 self.note_side_tx_failure(side, ty, attempts_total.saturating_add(attempts));
8233 return Err(err);
8234 }
8235 }
8236
8237 Ok(())
8238 })?;
8239 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
8240 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
8241
8242 #[cfg(feature = "compact-packet-store")]
8243 {
8244 let _ = bytes.park_for_queue();
8245 }
8246
8247 {
8248 let mut st = self.state.lock();
8249 let tx_state = self.reliable_tx_state_mut(&mut st, side, ty);
8250 tx_state.sent_order.push_back(seq);
8251 tx_state.sent.insert(
8252 seq,
8253 ReliableSent {
8254 bytes: bytes.clone(),
8255 last_send_ms: self.clock.now_ms(),
8256 retries: 0,
8257 queued: false,
8258 partial_acked: false,
8259 },
8260 );
8261 }
8262
8263 Ok(())
8264 }
8265
8266 fn run_side_chunk_transfer<T>(
8267 &self,
8268 side: RouterSideId,
8269 key: u64,
8270 send: impl FnOnce(&mut usize) -> TelemetryResult<T>,
8271 ) -> TelemetryResult<T> {
8272 let mut next = {
8273 let mut st = self.state.lock();
8274 let transport = st
8275 .side_transport
8276 .get_mut(&side)
8277 .ok_or(TelemetryError::BadArg)?;
8278 if transport.tx_chunk_progress.is_none() {
8279 crate::memory_admission::check(
8280 512,
8281 core::mem::size_of::<crate::side_transport::SideTxProgress>(),
8282 )?;
8283 transport.tx_chunk_progress =
8284 Some(Box::new(crate::side_transport::SideTxProgress::default()));
8285 }
8286 transport
8287 .tx_chunk_progress
8288 .as_mut()
8289 .expect("initialized")
8290 .begin(key, self.clock.now_ms())?
8291 };
8292 let result = send(&mut next);
8293 if let Some(progress) = self
8294 .state
8295 .lock()
8296 .side_transport
8297 .get_mut(&side)
8298 .and_then(|transport| transport.tx_chunk_progress.as_mut())
8299 {
8300 progress.finish(key, next, self.clock.now_ms(), &result);
8301 }
8302 result
8303 }
8304
8305 fn send_side_frames_resumable(
8306 &self,
8307 side: RouterSideId,
8308 mut frames: SideTransportFrames,
8309 mut send: impl FnMut(&[u8]) -> TelemetryResult<()>,
8310 ) -> TelemetryResult<()> {
8311 let bootstraps_template = frames.bootstraps_template();
8312 let result = if frames.len() == 1 {
8313 frames.send_resuming(&mut 0, send)
8314 } else {
8315 let key = frames.progress_key();
8316 self.run_side_chunk_transfer(side, key, |next| frames.send_resuming(next, &mut send))
8317 };
8318 if result.is_err() && bootstraps_template {
8319 if let Some(state) = self.state.lock().side_transport.get_mut(&side) {
8324 state.refresh_transmit_header_templates();
8325 }
8326 }
8327 result
8328 }
8329
8330 fn split_side_transport_frame(
8331 &self,
8332 _side: RouterSideId,
8333 frame: crate::SharedBytes,
8334 max_frame_bytes: usize,
8335 ) -> TelemetryResult<SideTransportFrames> {
8336 SideTransportFrames::split(frame, max_frame_bytes, self.sender_arc().as_bytes())
8337 }
8338
8339 fn encode_side_transport_frames(
8340 &self,
8341 side: RouterSideId,
8342 opts: RouterSideOptions,
8343 raw: crate::SharedBytes,
8344 ) -> TelemetryResult<SideTransportFrames> {
8345 if !opts.header_template_enabled && opts.max_frame_bytes == 0 {
8346 return Ok(SideTransportFrames::single(raw));
8347 }
8348
8349 let raw_len = raw.len();
8350 if opts.max_frame_bytes != 0 && raw_len > opts.max_frame_bytes {
8351 let frames = self.split_side_transport_frame(side, raw, opts.max_frame_bytes)?;
8354 let mut st = self.state.lock();
8355 let stats = st.side_runtime_stats.entry(side).or_default();
8356 stats.note_side_transport_full(raw_len, frames.wire_len());
8357 stats.note_side_transport_chunks(frames.len());
8358 return Ok(frames);
8359 }
8360
8361 if opts.header_template_enabled {
8362 let frame = wire_format::peek_routing_frame_info(raw.as_ref())?;
8366 let without_dictionary = opts.max_side_transport_templates == 0 || {
8367 let st = self.state.lock();
8368 self.side_has_multiple_announcers_locked(&st, side, self.clock.now_ms())
8369 };
8370 let reliable = is_reliable_type(frame.envelope.ty)
8371 || frame.reliable.is_some()
8372 || frame.envelope.target_senders().len() != 0
8378 || matches!(
8379 frame.envelope.ty,
8380 crate::DataType::ReliableAck
8381 | crate::DataType::ReliablePartialAck
8382 | crate::DataType::ReliablePacketRequest
8383 );
8384 if without_dictionary || reliable {
8385 let mut st = self.state.lock();
8386 if without_dictionary {
8387 let removed = st
8388 .side_transport
8389 .get_mut(&side)
8390 .ok_or(TelemetryError::BadArg)?
8391 .trim_tx_templates_to(0);
8392 for _ in 0..removed {
8393 st.side_runtime_stats
8394 .entry(side)
8395 .or_default()
8396 .note_side_transport_template_eviction();
8397 }
8398 }
8399 st.side_runtime_stats
8400 .entry(side)
8401 .or_default()
8402 .note_side_transport_full(raw_len, raw_len);
8403 return Ok(SideTransportFrames::single(raw));
8404 }
8405 }
8406
8407 #[cfg(any(feature = "std", feature = "fair-admission"))]
8408 if opts.header_template_enabled && crate::memory_admission::supports_work_kinds() {
8409 let (template, ..) = extract_side_header_template_view(raw.as_ref())?;
8410 let cached = self
8411 .state
8412 .lock()
8413 .side_transport
8414 .get(&side)
8415 .is_some_and(|transport| {
8416 transport.tx_template_ids.contains_key(&template.hash)
8417 && transport.tx_templates.contains_key(&template.hash)
8418 });
8419 if !cached
8420 && matches!(
8421 crate::memory_admission::check_receive(raw.as_ref()),
8422 Err(TelemetryError::Io("memory pressure"))
8423 )
8424 {
8425 self.state
8429 .lock()
8430 .side_runtime_stats
8431 .entry(side)
8432 .or_default()
8433 .note_side_transport_full(raw_len, raw_len);
8434 return Ok(SideTransportFrames::single(raw));
8435 }
8436 }
8437
8438 let mut compact_payload_len = None;
8439 let mut used_compact = false;
8440 let used_timestamp_delta = false;
8441 let omitted_timestamp = false;
8442 let wrapped = if opts.header_template_enabled {
8443 let (template, _ty, flags, data_size, timestamp, nonce, reliable_seq_ack, payload) =
8444 extract_side_header_template_view(raw.as_ref())?;
8445 let (template_id, use_compact, _previous_timestamp) = {
8448 let mut st = self.state.lock();
8449 let mut effective_template_limit = opts.max_side_transport_templates;
8454 #[cfg(feature = "discovery")]
8455 {
8456 if let Some(route) = st.discovery_routes.get(&side) {
8457 let active_peers = route
8458 .announcers
8459 .values()
8460 .filter(|sender| {
8461 self.clock.now_ms().saturating_sub(sender.last_seen_ms)
8462 <= DISCOVERY_ROUTE_TTL_MS
8463 })
8464 .count();
8465 if active_peers > 1 {
8473 effective_template_limit = 0;
8474 } else {
8475 effective_template_limit = route
8476 .announcers
8477 .values()
8478 .filter_map(|sender| sender.link_capabilities)
8479 .filter(|caps| {
8480 caps.flags & discovery::LINK_CAPABILITY_HEADER_TEMPLATES != 0
8481 && caps.max_side_transport_templates > 0
8482 })
8483 .map(|caps| caps.max_side_transport_templates as usize)
8484 .fold(effective_template_limit, core::cmp::min);
8485 }
8486 }
8487 }
8488 let trimmed = st
8489 .side_transport
8490 .get_mut(&side)
8491 .ok_or(TelemetryError::BadArg)?
8492 .trim_tx_templates_to(effective_template_limit);
8493 for _ in 0..trimmed {
8494 st.side_runtime_stats
8495 .entry(side)
8496 .or_default()
8497 .note_side_transport_template_eviction();
8498 }
8499 let side_state = st
8500 .side_transport
8501 .get_mut(&side)
8502 .ok_or(TelemetryError::BadArg)?;
8503 if let Some(id) = side_state.tx_template_ids.get(&template.hash).copied() {
8504 let previous = side_state.tx_last_timestamps.get(&id).copied();
8505 const FULL_REFRESH_AFTER_COMPACT_FRAMES: u8 = 8;
8508 let compact_uses = side_state.tx_compact_uses.entry(id).or_default();
8509 #[cfg(feature = "discovery")]
8511 let discovery_refresh = discovery::is_discovery_type(_ty);
8512 #[cfg(not(feature = "discovery"))]
8513 let discovery_refresh = false;
8514 let use_compact =
8515 *compact_uses < FULL_REFRESH_AFTER_COMPACT_FRAMES && !discovery_refresh;
8516 if use_compact {
8517 *compact_uses = compact_uses.saturating_add(1);
8518 } else {
8519 *compact_uses = 0;
8520 }
8521 (id, use_compact, previous)
8522 } else {
8523 let template_id = ((template.hash >> 32) as u32 ^ template.hash as u32).max(1);
8529 let evicted = side_state.insert_tx_template(
8530 template.into_owned(),
8531 template_id,
8532 effective_template_limit,
8533 );
8534 if evicted {
8535 st.side_runtime_stats
8536 .entry(side)
8537 .or_default()
8538 .note_side_transport_template_eviction();
8539 }
8540 if let Some(side_state) = st.side_transport.get_mut(&side)
8541 && effective_template_limit > 0
8542 {
8543 side_state.tx_last_timestamps.insert(template_id, timestamp);
8544 }
8545 (template_id, false, None)
8546 }
8547 };
8548 if use_compact {
8549 used_compact = true;
8550 compact_payload_len = Some(payload.len());
8551 let timestamp_field = Some(timestamp);
8554 let mut body = Vec::with_capacity(payload.len() + 32);
8555 body.push(flags);
8556 write_uleb128_local(u64::from(template_id), &mut body);
8557 write_uleb128_local(data_size, &mut body);
8558 if let Some(timestamp_field) = timestamp_field {
8559 write_uleb128_local(timestamp_field, &mut body);
8560 }
8561 if (flags & SIDE_TRANSPORT_FLAG_PACKET_NONCE) != 0 {
8562 write_uleb128_local(u64::from(nonce), &mut body);
8563 }
8564 if let Some((seq, ack)) = reliable_seq_ack {
8565 write_uleb128_local(u64::from(seq), &mut body);
8566 write_uleb128_local(u64::from(ack), &mut body);
8567 }
8568 body.extend_from_slice(payload);
8569 {
8570 let mut st = self.state.lock();
8571 if let Some(side_state) = st.side_transport.get_mut(&side) {
8572 side_state.tx_last_timestamps.insert(template_id, timestamp);
8573 }
8574 }
8575 let kind = if omitted_timestamp {
8576 SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP
8577 } else if used_timestamp_delta {
8578 SIDE_TRANSPORT_KIND_COMPACT_DELTA
8579 } else {
8580 SIDE_TRANSPORT_KIND_COMPACT
8581 };
8582 wrap_side_transport_frame(kind, &body)
8583 } else {
8584 let mut body = Vec::with_capacity(raw.len() + 4);
8585 write_uleb128_local(u64::from(template_id), &mut body);
8586 body.extend_from_slice(raw.as_ref());
8587 wrap_side_transport_frame(SIDE_TRANSPORT_KIND_FULL, &body)
8588 }
8589 } else {
8590 wrap_side_transport_frame(SIDE_TRANSPORT_KIND_FULL, raw.as_ref())
8591 };
8592
8593 let frames = if opts.max_frame_bytes != 0 && wrapped.len() > opts.max_frame_bytes {
8594 self.split_side_transport_frame(side, wrapped, opts.max_frame_bytes)
8595 } else {
8596 Ok(SideTransportFrames::single(wrapped))
8597 }?;
8598 let wire_len = frames.wire_len();
8599 let mut st = self.state.lock();
8600 let stats = st.side_runtime_stats.entry(side).or_default();
8601 if used_compact {
8602 let overhead = compact_payload_len
8603 .map(|payload_len| wire_len.saturating_sub(payload_len))
8604 .unwrap_or(wire_len);
8605 stats.note_side_transport_compact(
8606 raw_len,
8607 wire_len,
8608 overhead,
8609 used_timestamp_delta,
8610 omitted_timestamp,
8611 );
8612 if opts.compact_header_target_bytes != 0 && overhead > opts.compact_header_target_bytes
8613 {
8614 stats.note_side_transport_compact_target_miss();
8615 }
8616 } else {
8617 stats.note_side_transport_full(raw_len, wire_len);
8618 }
8619 if frames.len() > 1 {
8620 stats.note_side_transport_chunks(frames.len());
8621 }
8622 Ok(frames)
8623 }
8624
8625 fn expire_side_chunks(&self) {
8626 let now = self.clock.now_ms();
8627 let mut st = self.state.lock();
8628 for side in st.side_transport.values_mut() {
8629 side.rx_chunks
8630 .retain(|_, assembly| now.saturating_sub(assembly.last_seen_ms) <= 2000);
8631 }
8632 }
8633
8634 #[cfg(feature = "discovery")]
8638 fn refresh_ingress_peer_liveness(&self, side: RouterSideId, bytes: &[u8]) {
8639 let Ok(frame) = wire_format::peek_routing_frame_info(bytes) else {
8640 return;
8641 };
8642 let address = frame.envelope.source_address;
8643 self.refresh_peer_liveness(side, address);
8644 }
8645
8646 #[cfg(feature = "discovery")]
8647 #[cfg_attr(not(feature = "std"), inline(never))]
8648 fn refresh_peer_liveness(&self, side: RouterSideId, address: u32) {
8649 let now = self.clock.now_ms();
8650 let mut st = self.state.lock();
8651 let Some(route) = st.discovery_routes.get_mut(&side) else {
8652 return;
8653 };
8654 let mut found = None;
8655 for (name, peer) in &mut route.announcers {
8656 if peer.advertised_address == Some(address) || sender_address_u32(name) == address {
8657 if found.is_some() {
8659 return;
8660 }
8661 found = Some(peer);
8662 }
8663 }
8664 let Some(peer) = found else {
8665 return;
8666 };
8667 peer.last_seen_ms = now;
8668 route.last_seen_ms = route.last_seen_ms.max(now);
8669 }
8670
8671 #[cfg(all(feature = "discovery", not(feature = "std")))]
8672 fn consume_known_packed_schema(
8673 &self,
8674 side: RouterSideId,
8675 bytes: &[u8],
8676 ) -> TelemetryResult<bool> {
8677 let Some((address, payload)) = wire_format::peek_plain_schema_payload(bytes)? else {
8678 return Ok(false);
8679 };
8680 if !discovery::discovery_schema_payload_is_fully_known(payload)? {
8681 return Ok(false);
8682 }
8683 let mut st = self.state.lock();
8686 if let Some(route) = st.discovery_routes.get_mut(&side) {
8687 for (name, peer) in &mut route.announcers {
8688 if peer.advertised_address == Some(address) || sender_address_u32(name) == address {
8689 peer.has_schema = true;
8690 break;
8691 }
8692 }
8693 }
8694 Ok(true)
8695 }
8696
8697 #[cfg(feature = "discovery")]
8700 fn cached_transit_ready(&self, side: RouterSideId, bytes: &[u8]) -> TelemetryResult<bool> {
8701 if bytes.len() > 128 || bytes.first().is_none_or(|flags| flags & 0x15 != 0) {
8702 return Ok(false);
8703 }
8704 let frame = wire_format::peek_routing_frame_info(bytes)?;
8705 let ty = frame.envelope.ty;
8706 if is_internal_control_type(ty)
8707 || frame.envelope.is_reliable
8708 || frame.reliable.is_some()
8709 || frame.envelope.target_senders().len() != 0
8710 || !(1..=3).contains(&frame.envelope.endpoints().take(4).count())
8711 {
8712 return Ok(false);
8713 }
8714 if self.is_managed_variable_type(ty)
8715 || frame.envelope.endpoints().any(|ep| {
8716 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
8717 })
8718 {
8719 return Ok(false);
8720 }
8721
8722 let (template, ..) = extract_side_header_template_view(bytes)?;
8723 let st = self.state.lock();
8724 let active: usize = st.sides.iter().filter(|entry| entry.is_some()).count();
8725 if active != 2 {
8726 return Ok(false);
8727 }
8728 for (id, entry) in st
8729 .sides
8730 .iter()
8731 .enumerate()
8732 .filter_map(|(id, entry)| entry.as_ref().map(|e| (id, e)))
8733 {
8734 if entry.opts.reliable_enabled
8735 || !matches!(
8736 entry.tx_handler,
8737 RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)
8738 )
8739 || !st
8740 .side_runtime_stats
8741 .get(&id)
8742 .is_some_and(|stats| stats.data_types.contains_key(&ty.as_u32()))
8743 {
8744 return Ok(false);
8745 }
8746 if id == side {
8747 continue;
8748 }
8749 #[cfg(feature = "discovery")]
8750 if !st.discovery_routes.get(&id).is_some_and(|route| {
8751 self.clock.now_ms().saturating_sub(route.last_seen_ms) <= DISCOVERY_ROUTE_TTL_MS
8752 && frame
8753 .envelope
8754 .endpoints()
8755 .any(|ep| route.reachable.contains(&ep))
8756 }) {
8757 return Ok(false);
8758 }
8759 if entry.opts.header_template_enabled
8760 && entry.opts.max_frame_bytes != 0
8761 && entry.opts.max_side_transport_templates != 0
8762 && !self.side_has_multiple_announcers_locked(&st, id, self.clock.now_ms())
8763 && !st.side_transport.get(&id).is_some_and(|transport| {
8764 transport.tx_template_ids.contains_key(&template.hash)
8765 && transport.tx_templates.contains_key(&template.hash)
8766 })
8767 {
8768 #[cfg(any(feature = "std", feature = "fair-admission"))]
8773 if (entry.opts.max_frame_bytes == 0 || bytes.len() <= entry.opts.max_frame_bytes)
8774 && st.discovery_routes.get(&side).is_some_and(|route| {
8775 route.announcers.iter().any(|(name, peer)| {
8776 peer.advertised_address == Some(frame.envelope.source_address)
8777 || sender_address_u32(name) == frame.envelope.source_address
8778 })
8779 })
8780 {
8781 continue;
8782 }
8783 return Ok(false);
8784 }
8785 }
8786 Ok(true)
8787 }
8788 #[cfg(not(feature = "discovery"))]
8789 fn cached_transit_ready(&self, _side: RouterSideId, _bytes: &[u8]) -> TelemetryResult<bool> {
8790 Ok(false)
8791 }
8792 #[cfg(any(feature = "std", feature = "fair-admission"))]
8793 fn reclaim_for_receive(&self, bytes: &[u8], victim: &mut Option<(u32, u8)>) -> bool {
8794 let Some((incoming, priority)) = crate::admission_fairness::incoming(bytes) else {
8795 return false;
8796 };
8797 self.reclaim_for_incoming(incoming, priority, victim)
8798 }
8799 #[cfg(any(feature = "std", feature = "fair-admission"))]
8800 fn reclaim_for_incoming(
8801 &self,
8802 incoming: u32,
8803 priority: u8,
8804 victim: &mut Option<(u32, u8)>,
8805 ) -> bool {
8806 let mut st = self.state.lock();
8807 let (owner, priority) = if let Some(selected) = *victim {
8808 selected
8809 } else {
8810 let Some(owner) = st.competing_bulk_owner(incoming, priority) else {
8811 return false;
8812 };
8813 *victim = Some((owner, priority));
8814 (owner, priority)
8815 };
8816 st.reclaim_bulk_from(owner, priority, None)
8817 }
8818 fn admit_received_frame(&self, side: RouterSideId, bytes: &[u8]) -> TelemetryResult<()> {
8819 if !crate::memory_admission::has_probe() {
8820 return Ok(());
8821 }
8822 #[cfg(any(feature = "std", feature = "fair-admission"))]
8823 let mut fair_victim = None;
8824 if crate::memory_admission::supports_work_kinds()
8825 && self.cached_transit_ready(side, bytes)?
8826 {
8827 let result = crate::memory_admission::check_cached_transit(bytes.len());
8828 #[cfg(any(feature = "std", feature = "fair-admission"))]
8829 let result = {
8830 let mut result = result;
8831 while matches!(result, Err(TelemetryError::Io("memory pressure"))) {
8832 if !self.reclaim_for_receive(bytes, &mut fair_victim) {
8833 break;
8834 }
8835 result = crate::memory_admission::check_cached_transit(bytes.len());
8836 }
8837 result
8838 };
8839 result
8840 } else {
8841 let managed = wire_format::scheduling_type(bytes)
8842 .is_some_and(|ty| self.is_managed_variable_type(ty));
8843 let mut protected = None;
8844 loop {
8845 match crate::memory_admission::check_receive_with_role(bytes, managed) {
8846 Err(TelemetryError::Io("memory pressure")) => {
8847 let protected = *protected.get_or_insert_with(|| {
8848 wire_format::peek_routing_frame_info(bytes)
8849 .ok()
8850 .is_some_and(|frame| {
8851 managed || message_priority(frame.envelope.ty) >= 200
8852 })
8853 });
8854 if !protected {
8855 #[cfg(any(feature = "std", feature = "fair-admission"))]
8856 if self.reclaim_for_receive(bytes, &mut fair_victim) {
8857 continue;
8858 }
8859 return Err(TelemetryError::Io("memory pressure"));
8860 }
8861 let mut st = self.state.lock();
8865 let reclaimed = st
8866 .lower_priority_shared_queue(199)
8867 .is_some_and(|queue| st.pop_shared_queue_item(queue));
8868 if !reclaimed {
8869 return Err(TelemetryError::Io("memory pressure"));
8870 }
8871 }
8872 result => return result,
8873 }
8874 }
8875 }
8876 }
8877
8878 fn decode_side_transport_frame(
8879 &self,
8880 side: RouterSideId,
8881 bytes: &[u8],
8882 ) -> TelemetryResult<Option<crate::SharedBytes>> {
8883 self.expire_side_chunks();
8885 let Some((kind, body)) = parse_side_transport_wrapper(bytes)? else {
8886 #[cfg(feature = "discovery")]
8887 self.refresh_ingress_peer_liveness(side, bytes);
8888 #[cfg(all(feature = "discovery", not(feature = "std")))]
8889 if self.consume_known_packed_schema(side, bytes)? {
8890 return Ok(None);
8891 }
8892 self.admit_received_frame(side, bytes)?;
8893 return Ok(Some(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes))));
8894 };
8895 match kind {
8896 SIDE_TRANSPORT_KIND_FULL => {
8897 let header_templates_enabled = self
8898 .state
8899 .lock()
8900 .sides
8901 .get(side)
8902 .and_then(|entry| entry.as_ref())
8903 .map(|side| side.opts.header_template_enabled)
8904 .unwrap_or(false);
8905 let mut off = 0usize;
8906 let template_id = if header_templates_enabled {
8907 Some(
8908 u32::try_from(read_uleb128_local(body, &mut off)?)
8909 .map_err(|_| TelemetryError::Unpack("side template id too large"))?,
8910 )
8911 } else {
8912 None
8913 };
8914 #[cfg(feature = "discovery")]
8915 self.refresh_ingress_peer_liveness(side, &body[off..]);
8916 #[cfg(all(feature = "discovery", not(feature = "std")))]
8917 if self.consume_known_packed_schema(side, &body[off..])? {
8918 return Ok(None);
8919 }
8920 self.admit_received_frame(side, &body[off..])?;
8921 let raw = Arc::<[u8]>::from(&body[off..]);
8922 if let (Some(template_id), Ok((template, _, _, _, timestamp, _, _, _))) =
8923 (template_id, extract_side_header_template(raw.as_ref()))
8924 {
8925 let mut st = self.state.lock();
8926 let max_templates = st
8927 .sides
8928 .get(side)
8929 .and_then(|side| side.as_ref())
8930 .map(|side| side.opts.max_side_transport_templates)
8931 .unwrap_or(DEFAULT_SIDE_TRANSPORT_TEMPLATE_LIMIT);
8932 let evicted = st.side_transport.get_mut(&side).is_some_and(|side_state| {
8933 let evicted =
8934 side_state.insert_rx_template(template_id, template, max_templates);
8935 if max_templates > 0 {
8936 side_state.rx_last_timestamps.insert(template_id, timestamp);
8937 }
8938 evicted
8939 });
8940 if evicted {
8941 st.side_runtime_stats
8942 .entry(side)
8943 .or_default()
8944 .note_side_transport_template_eviction();
8945 }
8946 }
8947 Ok(Some(crate::shared_bytes::convert(raw)))
8948 }
8949 SIDE_TRANSPORT_KIND_COMPACT
8950 | SIDE_TRANSPORT_KIND_COMPACT_DELTA
8951 | SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP => {
8952 if body.is_empty() {
8953 return Err(TelemetryError::Unpack("short side compact frame"));
8954 }
8955 let mut off = 1usize;
8956 let template_id = u32::try_from(read_uleb128_local(body, &mut off)?)
8957 .map_err(|_| TelemetryError::Unpack("side template id too large"))?;
8958 let (template, timestamp_base) = {
8959 let st = self.state.lock();
8960 let state = st.side_transport.get(&side);
8961 let template = state
8962 .and_then(|state| state.rx_templates_by_id.get(&template_id))
8963 .cloned();
8964 let timestamp_base = if matches!(
8965 kind,
8966 SIDE_TRANSPORT_KIND_COMPACT_DELTA
8967 | SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP
8968 ) {
8969 state
8970 .and_then(|state| state.rx_last_timestamps.get(&template_id))
8971 .copied()
8972 } else {
8973 None
8974 };
8975 (template, timestamp_base)
8976 };
8977 let Some(template) = template else {
8981 return Ok(None);
8982 };
8983 #[cfg(feature = "discovery")]
8986 if let Some(address) = template.source_address() {
8987 self.refresh_peer_liveness(side, address);
8988 }
8989 let reconstructed_bound = body
8990 .len()
8991 .saturating_add(template.prefix.len())
8992 .saturating_add(template.between.len())
8993 .saturating_add(32);
8994 let protected = template.data_type().is_some_and(|ty| {
8998 message_priority(ty) >= 200 || self.is_managed_variable_type(ty)
8999 });
9000 let ack = template.is_ack();
9001 #[cfg(any(feature = "std", feature = "fair-admission"))]
9002 let mut fair_incoming = None;
9003 #[cfg(any(feature = "std", feature = "fair-admission"))]
9004 let mut fair_victim = None;
9005 loop {
9006 match crate::memory_admission::check_frame(reconstructed_bound, ack) {
9007 Err(TelemetryError::Io("memory pressure")) if protected => {
9008 let mut st = self.state.lock();
9009 let reclaimed = st
9010 .lower_priority_shared_queue(199)
9011 .is_some_and(|queue| st.pop_shared_queue_item(queue));
9012 if !reclaimed {
9013 return Err(TelemetryError::Io("memory pressure"));
9014 }
9015 }
9016 Err(TelemetryError::Io("memory pressure")) => {
9017 #[cfg(any(feature = "std", feature = "fair-admission"))]
9021 if let Some((incoming, priority)) =
9022 *fair_incoming.get_or_insert_with(|| {
9023 crate::admission_fairness::compact_incoming(&template)
9024 })
9025 && self.reclaim_for_incoming(incoming, priority, &mut fair_victim)
9026 {
9027 continue;
9028 }
9029 return Err(TelemetryError::Io("memory pressure"));
9030 }
9031 result => {
9032 result?;
9033 break;
9034 }
9035 }
9036 }
9037 let timestamp_mode = match kind {
9038 SIDE_TRANSPORT_KIND_COMPACT_DELTA => SideCompactTimestampMode::Delta,
9039 SIDE_TRANSPORT_KIND_COMPACT_SAME_TIMESTAMP => SideCompactTimestampMode::Omitted,
9040 _ => SideCompactTimestampMode::Absolute,
9041 };
9042 let (frame, timestamp) = reconstruct_side_compact_frame(
9043 &template,
9044 body[0],
9045 &body[off..],
9046 timestamp_mode,
9047 timestamp_base,
9048 )?;
9049 let mut st = self.state.lock();
9050 if let Some(side_state) = st.side_transport.get_mut(&side) {
9051 side_state.rx_last_timestamps.insert(template_id, timestamp);
9052 }
9053 Ok(Some(frame))
9054 }
9055 SIDE_TRANSPORT_KIND_CHUNK => {
9056 crate::memory_admission::check_receive(bytes)?;
9057 if body.len() < 8 {
9058 return Err(TelemetryError::Unpack("short side chunk frame"));
9059 }
9060 let transfer_id = u32::from_le_bytes([body[0], body[1], body[2], body[3]]);
9061 let index = u16::from_le_bytes([body[4], body[5]]);
9062 let total = u16::from_le_bytes([body[6], body[7]]);
9063 if total == 0 || index >= total {
9064 return Err(TelemetryError::Unpack("invalid side chunk index"));
9065 }
9066 let assembled = {
9067 let mut st = self.state.lock();
9068 let budget = st.memory.max_queue_budget;
9069 let now_ms = self.clock.now_ms();
9070 let side_state = st
9071 .side_transport
9072 .get_mut(&side)
9073 .ok_or(TelemetryError::BadArg)?;
9074 side_state
9075 .rx_chunks
9076 .retain(|_, assembly| now_ms.saturating_sub(assembly.last_seen_ms) <= 2000);
9077 if !side_state.rx_chunks.contains_key(&transfer_id)
9078 && side_state.rx_chunks.len() >= 4
9079 && let Some(oldest) = side_state
9080 .rx_chunks
9081 .iter()
9082 .min_by_key(|(_, a)| a.last_seen_ms)
9083 .map(|(id, _)| *id)
9084 {
9085 side_state.rx_chunks.remove(&oldest);
9086 }
9087 SideChunkAssembly::make_room(
9088 &mut side_state.rx_chunks,
9089 transfer_id,
9090 index,
9091 total,
9092 body.len() - 8,
9093 budget,
9094 )?;
9095 let entry = side_state.rx_chunks.entry(transfer_id).or_default();
9096 entry.last_seen_ms = now_ms;
9097 if entry.total == 0 {
9098 entry.total = total;
9099 } else if entry.total != total {
9100 side_state.rx_chunks.remove(&transfer_id);
9101 return Err(TelemetryError::Unpack("side chunk total mismatch"));
9102 }
9103 entry.received.entry(index).or_insert_with(|| {
9104 crate::shared_bytes::convert(Arc::<[u8]>::from(&body[8..]))
9105 });
9106 if entry.received.len() == usize::from(total) {
9107 let entry = side_state
9108 .rx_chunks
9109 .remove(&transfer_id)
9110 .ok_or(TelemetryError::Unpack("side chunk missing"))?;
9111 Some(entry.assemble()?)
9112 } else {
9113 None
9114 }
9115 };
9116 match assembled {
9117 Some(frame) => self.decode_side_transport_frame(side, frame.as_ref()),
9118 None => Ok(None),
9119 }
9120 }
9121 _ => Err(TelemetryError::Unpack("unknown side transport frame")),
9122 }
9123 }
9124
9125 fn packed_handler_call(
9126 handler: &RouterTxHandlerFn,
9127 bytes: &[u8],
9128 priority: u8,
9129 ) -> TelemetryResult<()> {
9130 match handler {
9131 RouterTxHandlerFn::Packed(handler) => handler(bytes),
9132 RouterTxHandlerFn::PackedWithPriority(handler) => handler(bytes, priority),
9133 RouterTxHandlerFn::Packet(_) => Err(TelemetryError::BadArg),
9134 }
9135 }
9136
9137 fn effective_transport_priority(&self, ty: DataType) -> u8 {
9138 if self.is_managed_variable_type(ty) {
9139 crate::transport_priority(DataType::ManagedVariableValue)
9140 } else {
9141 crate::transport_priority(ty)
9142 }
9143 }
9144
9145 fn call_side_tx_handler(
9146 &self,
9147 side: RouterSideId,
9148 handler: &RouterTxHandlerFn,
9149 data: &RouterItem,
9150 relayed: bool,
9151 ) -> TelemetryResult<()> {
9152 let opts = {
9153 let st = self.state.lock();
9154 Self::side_ref(&st, side)?.opts.clone()
9155 };
9156 let Some(_side_tx_guard) = self.try_enter_side_tx() else {
9157 return Err(TelemetryError::Io("side tx busy"));
9158 };
9159 let started_ms = self.clock.now_ms();
9160 let ty = match data {
9161 RouterItem::Packet(pkt) => pkt.data_type(),
9162 RouterItem::Packed(bytes) => wire_format::peek_routing_envelope(bytes.as_ref())?.ty,
9163 };
9164 let priority = self.item_service_priority(data)?;
9165 let result = match (handler, data) {
9166 (
9167 packed @ (RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)),
9168 RouterItem::Packed(bytes),
9169 ) => {
9170 #[cfg(feature = "cryptography")]
9171 let send_bytes = self.prepare_packed_for_remote(bytes.clone(), None)?;
9172 #[cfg(not(feature = "cryptography"))]
9173 let send_bytes = bytes.clone();
9174 let frames = self.encode_side_transport_frames(side, opts, send_bytes)?;
9175 let mut attempts_total = 0usize;
9176 let mut sent_bytes = 0usize;
9177 self.send_side_frames_resumable(side, frames, |frame| {
9178 match self.retry_with_attempts(runtime_max_handler_retries(), || {
9179 Self::packed_handler_call(packed, frame, priority)
9180 }) {
9181 Ok((_, attempts)) => {
9182 attempts_total = attempts_total.saturating_add(attempts);
9183 sent_bytes = sent_bytes.saturating_add(frame.len());
9184 }
9185 Err((err, attempts)) => {
9186 self.note_side_tx_failure(
9187 side,
9188 ty,
9189 attempts_total.saturating_add(attempts),
9190 );
9191 return Err(err);
9192 }
9193 }
9194
9195 Ok(())
9196 })?;
9197 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
9198 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
9199 return Ok(());
9200 }
9201 (RouterTxHandlerFn::Packet(f), RouterItem::Packet(pkt)) => {
9202 self.retry_with_attempts(runtime_max_handler_retries(), || f(pkt))
9203 }
9204 (
9205 packed @ (RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)),
9206 RouterItem::Packet(pkt),
9207 ) => {
9208 if ty == DataType::DiscoverySchema
9212 && opts.max_frame_bytes != 0
9213 && pkt.payload().len() > opts.max_frame_bytes
9214 {
9215 let mut attempts_total = 0usize;
9216 let mut sent_bytes = 0usize;
9217 let key = crate::packet::hash_bytes_u64(
9218 pkt.packet_id(),
9219 &opts.max_frame_bytes.to_le_bytes(),
9220 );
9221 let result = self.run_side_chunk_transfer(side, key, |next| {
9222 crate::side_transport::send_packet_chunks(
9223 pkt,
9224 opts.max_frame_bytes,
9225 self.sender_arc().as_bytes(),
9226 next,
9227 |frame| match self
9228 .retry_with_attempts(runtime_max_handler_retries(), || {
9229 Self::packed_handler_call(packed, frame, priority)
9230 }) {
9231 Ok((_, attempts)) => {
9232 attempts_total += attempts;
9233 sent_bytes += frame.len();
9234 Ok(())
9235 }
9236 Err((err, attempts)) => {
9237 attempts_total += attempts;
9238 Err(err)
9239 }
9240 },
9241 )
9242 });
9243 let (raw_len, wire_len, chunks) = match result {
9244 Ok(stats) => stats,
9245 Err(err) => {
9246 self.note_side_tx_failure(side, ty, attempts_total);
9247 return Err(err);
9248 }
9249 };
9250 {
9251 let mut st = self.state.lock();
9252 let stats = st.side_runtime_stats.entry(side).or_default();
9253 stats.note_side_transport_full(raw_len, wire_len);
9254 if chunks > 1 {
9255 stats.note_side_transport_chunks(chunks);
9256 }
9257 }
9258 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
9259 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
9260 return Ok(());
9261 }
9262 let owned = self.pack_packet_for_router(pkt, None)?;
9263 let frames = self.encode_side_transport_frames(side, opts, owned)?;
9264 let mut attempts_total = 0usize;
9265 let mut sent_bytes = 0usize;
9266 self.send_side_frames_resumable(side, frames, |frame| {
9267 match self.retry_with_attempts(runtime_max_handler_retries(), || {
9268 Self::packed_handler_call(packed, frame, priority)
9269 }) {
9270 Ok((_, attempts)) => {
9271 attempts_total = attempts_total.saturating_add(attempts);
9272 sent_bytes = sent_bytes.saturating_add(frame.len());
9273 }
9274 Err((err, attempts)) => {
9275 self.note_side_tx_failure(
9276 side,
9277 ty,
9278 attempts_total.saturating_add(attempts),
9279 );
9280 return Err(err);
9281 }
9282 }
9283
9284 Ok(())
9285 })?;
9286 self.record_side_tx_sample(side, sent_bytes, started_ms, self.clock.now_ms());
9287 self.note_side_tx_success(side, ty, sent_bytes, relayed, attempts_total);
9288 return Ok(());
9289 }
9290 (RouterTxHandlerFn::Packet(f), RouterItem::Packed(bytes)) => {
9291 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
9292 self.retry_with_attempts(runtime_max_handler_retries(), || f(&pkt))
9293 }
9294 };
9295 match result {
9296 Ok((_, attempts)) => {
9297 if let Ok(bytes) = Self::router_item_wire_len(data) {
9298 self.record_side_tx_sample(side, bytes, started_ms, self.clock.now_ms());
9299 self.note_side_tx_success(side, ty, bytes, relayed, attempts);
9300 }
9301 Ok(())
9302 }
9303 Err((err, attempts)) => {
9304 self.note_side_tx_failure(side, ty, attempts);
9305 Err(err)
9306 }
9307 }
9308 }
9309
9310 fn adjust_reliable_for_side(
9311 &self,
9312 opts: RouterSideOptions,
9313 data: RouterItem,
9314 preserve_end_to_end_ack: bool,
9315 ) -> TelemetryResult<Option<RouterItem>> {
9316 if opts.reliable_enabled {
9317 return Ok(Some(data));
9318 }
9319
9320 match data {
9321 RouterItem::Packed(bytes) => {
9322 let frame = wire_format::peek_routing_frame_info(bytes.as_ref())?;
9323 if is_reliable_type(frame.envelope.ty)
9324 && let Some(hdr) = frame.reliable
9325 {
9326 if (hdr.flags & wire_format::RELIABLE_FLAG_ACK_ONLY) != 0 {
9327 return Ok(None);
9328 }
9329 if (hdr.flags & wire_format::RELIABLE_FLAG_UNSEQUENCED) == 0 {
9330 let Some(rewritten) = wire_format::rewrite_reliable_header_owned(
9331 bytes.as_ref(),
9332 wire_format::RELIABLE_FLAG_UNSEQUENCED,
9333 hdr.seq,
9334 0,
9335 )?
9336 else {
9337 return Ok(Some(RouterItem::Packed(bytes)));
9338 };
9339 return Ok(Some(RouterItem::Packed(crate::shared_bytes::convert(
9340 rewritten,
9341 ))));
9342 }
9343 }
9344 Ok(Some(RouterItem::Packed(bytes)))
9345 }
9346 RouterItem::Packet(pkt) => {
9347 if matches!(
9348 pkt.data_type(),
9349 DataType::ReliableAck
9350 | DataType::ReliablePartialAck
9351 | DataType::ReliablePacketRequest
9352 ) {
9353 if preserve_end_to_end_ack && Self::is_end_to_end_ack_packet(&pkt) {
9354 return Ok(Some(RouterItem::Packet(pkt)));
9355 }
9356 return Ok(None);
9357 }
9358 Ok(Some(RouterItem::Packet(pkt)))
9359 }
9360 }
9361 }
9362
9363 fn process_reliable_timeouts(&self) -> TelemetryResult<()> {
9364 let now = self.clock.now_ms();
9365 let mut requeue: Vec<(RouterSideId, DataType, u32)> = Vec::new();
9366
9367 {
9368 let mut st = self.state.lock();
9369 if st.reliable_tx.is_empty() {
9370 return Ok(());
9371 }
9372
9373 for ((side, ty_u32), tx_state) in st.reliable_tx.iter_mut() {
9374 let Some(ty) = DataType::try_from_u32(*ty_u32) else {
9375 continue;
9376 };
9377 let sent_order: Vec<u32> = tx_state.sent_order.iter().copied().collect();
9378 for seq in sent_order {
9379 let Some(sent) = tx_state.sent.get_mut(&seq) else {
9380 continue;
9381 };
9382 if sent.queued
9383 || now.wrapping_sub(sent.last_send_ms) < runtime_reliable_retransmit_ms()
9384 {
9385 continue;
9386 }
9387 if sent.partial_acked {
9388 sent.partial_acked = false;
9395 sent.last_send_ms = now;
9396 continue;
9397 }
9398 if sent.retries >= runtime_reliable_max_retries() {
9399 tx_state.sent.remove(&seq);
9400 tx_state.sent_order.retain(|existing| *existing != seq);
9401 continue;
9402 }
9403 sent.retries += 1;
9404 requeue.push((*side, ty, seq));
9405 }
9406 }
9407 }
9408
9409 for (side, ty, seq) in requeue {
9410 self.queue_reliable_retransmit(side, ty, seq, true)?;
9411 }
9412
9413 Ok(())
9414 }
9415
9416 fn process_end_to_end_reliable_timeouts(&self) -> TelemetryResult<()> {
9417 let now = self.clock.now_ms();
9418 let mut requeue = Vec::new();
9419 let mut failed_ordered_types = BTreeSet::new();
9420
9421 {
9422 let mut st = self.state.lock();
9423 #[cfg(feature = "discovery")]
9424 {
9425 if Self::prune_discovery_routes_locked(&mut st, now) {
9426 Self::note_discovery_topology_change_locked(&mut st, now);
9427 self.reconcile_end_to_end_reliable_destinations_locked(&mut st)?;
9428 }
9429 }
9434 st.end_to_end_reliable_tx.retain(|&packet_id, sent| {
9437 if sent.waiting_for_predecessor
9438 || sent.queued
9439 || now.wrapping_sub(sent.last_send_ms) < runtime_reliable_retransmit_ms()
9440 {
9441 return true;
9442 }
9443 if sent.retries >= runtime_reliable_max_retries() {
9444 if let Some(ty) = sent.ordered_type {
9445 failed_ordered_types.insert(ty);
9446 }
9447 return false;
9448 }
9449 sent.retries += 1;
9450 requeue.push(packet_id);
9451 true
9452 });
9453 st.end_to_end_reliable_tx.retain(|_, sent| {
9455 !sent
9456 .ordered_type
9457 .is_some_and(|ty| failed_ordered_types.contains(&ty))
9458 });
9459 }
9460
9461 for packet_id in requeue {
9462 self.queue_end_to_end_reliable_retransmit(packet_id)?;
9463 }
9464 self.release_ordered_successors()?;
9465 if !failed_ordered_types.is_empty() {
9466 return Err(TelemetryError::HandlerError(
9467 "ordered end-to-end delivery exhausted retries",
9468 ));
9469 }
9470 Ok(())
9471 }
9472
9473 #[cfg(feature = "timesync")]
9474 #[inline]
9475 fn monotonic_now_ns(&self) -> u64 {
9476 self.clock.now_ns()
9477 }
9478
9479 #[cfg(feature = "timesync")]
9480 #[inline]
9481 fn monotonic_now_ms(&self) -> u64 {
9482 self.clock.now_ms()
9483 }
9484
9485 #[cfg(feature = "timesync")]
9486 fn refresh_timesync_state(&self, now_mono_ms: u64) {
9487 let now_mono_ns = self.monotonic_now_ns();
9488 let mut st = self.timesync.lock();
9489 st.clock.prune_expired(now_mono_ms);
9490 let timeout_ms = st.cfg.map(|cfg| cfg.source_timeout_ms).unwrap_or(0);
9491 st.remote_sources
9492 .retain(|_, src| now_mono_ms.saturating_sub(src.last_sample_mono_ms) <= timeout_ms);
9493 let has_usable_time = Self::timesync_has_usable_time_locked(&st, now_mono_ns);
9494 let leader = if let Some(tracker) = st.tracker.as_mut() {
9495 let _ = tracker.refresh(now_mono_ms);
9496 tracker.leader(now_mono_ms, has_usable_time)
9497 } else {
9498 None
9499 };
9500 Self::reconcile_pending_timesync_request_locked(&mut st, &leader, now_mono_ms);
9501 if let Some(TimeSyncLeader::Remote(remote)) = leader.as_ref() {
9502 let target_ms = st
9503 .remote_sources
9504 .get(remote.sender.as_str())
9505 .map(|src| src.sample_unix_ms);
9506 if let Some(target_ms) = target_ms {
9507 st.disciplined_clock.steer_unix_ms(now_mono_ns, target_ms);
9508 }
9509 }
9510 }
9511
9512 #[cfg(feature = "timesync")]
9513 pub fn update_network_time_source(
9515 &self,
9516 source: &str,
9517 priority: u64,
9518 time: PartialNetworkTime,
9519 ttl_ms: Option<u64>,
9520 ) {
9521 let now_ms = self.monotonic_now_ms();
9522 let now_ns = self.monotonic_now_ns();
9523 let mut st = self.timesync.lock();
9524 st.clock
9525 .update_source(source, priority, time, now_ms, now_ns, ttl_ms);
9526 if let Some(unix_ms) = time.to_network_time().and_then(|t| t.as_unix_ms()) {
9527 st.disciplined_clock.steer_unix_ms(now_ns, unix_ms);
9528 }
9529 }
9530
9531 #[cfg(feature = "timesync")]
9532 fn set_network_time_source_impl(
9533 &self,
9534 source: &str,
9535 priority: u64,
9536 time: PartialNetworkTime,
9537 ttl_ms: Option<u64>,
9538 ) {
9539 let observed_mono_ms = self.monotonic_now_ms();
9540 let observed_mono_ns = self.monotonic_now_ns();
9541 let mut st = self.timesync.lock();
9542 let commit_mono_ms = self.monotonic_now_ms();
9543 let commit_mono_ns = self.monotonic_now_ns();
9544 let adjusted = if let Some(base) = time.to_network_time() {
9545 let elapsed_ns = commit_mono_ns.saturating_sub(observed_mono_ns);
9546 advance_network_time(base, elapsed_ns)
9547 .map(PartialNetworkTime::from)
9548 .unwrap_or(time)
9549 } else {
9550 time
9551 };
9552 let adjusted_mono_ms =
9553 observed_mono_ms.saturating_add(commit_mono_ms.saturating_sub(observed_mono_ms));
9554 st.clock.update_source(
9555 source,
9556 priority,
9557 adjusted,
9558 commit_mono_ms.max(adjusted_mono_ms),
9559 commit_mono_ns,
9560 ttl_ms,
9561 );
9562 if let Some(unix_ms) = adjusted.to_network_time().and_then(|t| t.as_unix_ms()) {
9563 st.disciplined_clock.steer_unix_ms(commit_mono_ns, unix_ms);
9564 }
9565 }
9566
9567 #[cfg(feature = "timesync")]
9568 fn local_network_time_priority(&self) -> u64 {
9569 let st = self.timesync.lock();
9570 st.cfg.map(|cfg| cfg.priority).unwrap_or(0)
9571 }
9572
9573 #[cfg(feature = "timesync")]
9574 pub fn set_local_network_time(&self, time: PartialNetworkTime) {
9576 let priority = self.local_network_time_priority();
9577 if time.is_complete_date() && time.is_complete_time() {
9578 self.set_network_time_source_impl(LOCAL_TIMESYNC_FULL_SOURCE_ID, priority, time, None);
9579 let mut st = self.timesync.lock();
9580 st.clock.remove_source(LOCAL_TIMESYNC_DATE_SOURCE_ID);
9581 st.clock.remove_source(LOCAL_TIMESYNC_TOD_SOURCE_ID);
9582 st.clock.remove_source(LOCAL_TIMESYNC_SUBSEC_SOURCE_ID);
9583 return;
9584 }
9585
9586 {
9587 let mut st = self.timesync.lock();
9588 st.clock.remove_source(LOCAL_TIMESYNC_FULL_SOURCE_ID);
9589 }
9590
9591 if time.year.is_some() || time.month.is_some() || time.day.is_some() {
9592 self.set_network_time_source_impl(
9593 LOCAL_TIMESYNC_DATE_SOURCE_ID,
9594 priority,
9595 PartialNetworkTime {
9596 year: time.year,
9597 month: time.month,
9598 day: time.day,
9599 ..Default::default()
9600 },
9601 None,
9602 );
9603 }
9604
9605 if time.hour.is_some() || time.minute.is_some() || time.second.is_some() {
9606 self.set_network_time_source_impl(
9607 LOCAL_TIMESYNC_TOD_SOURCE_ID,
9608 priority,
9609 PartialNetworkTime {
9610 hour: time.hour,
9611 minute: time.minute,
9612 second: time.second,
9613 nanosecond: time.nanosecond,
9614 ..Default::default()
9615 },
9616 None,
9617 );
9618 }
9619
9620 if time.nanosecond.is_some() {
9621 self.set_network_time_source_impl(
9622 LOCAL_TIMESYNC_SUBSEC_SOURCE_ID,
9623 priority,
9624 PartialNetworkTime {
9625 nanosecond: time.nanosecond,
9626 ..Default::default()
9627 },
9628 None,
9629 );
9630 }
9631 }
9632
9633 #[cfg(feature = "timesync")]
9634 pub fn clear_local_network_time(&self) {
9636 let mut st = self.timesync.lock();
9637 st.clock.remove_source(LOCAL_TIMESYNC_FULL_SOURCE_ID);
9638 st.clock.remove_source(LOCAL_TIMESYNC_DATE_SOURCE_ID);
9639 st.clock.remove_source(LOCAL_TIMESYNC_TOD_SOURCE_ID);
9640 st.clock.remove_source(LOCAL_TIMESYNC_SUBSEC_SOURCE_ID);
9641 }
9642
9643 #[cfg(feature = "timesync")]
9644 pub fn set_local_network_date(&self, year: i32, month: u8, day: u8) {
9646 self.set_local_network_time(PartialNetworkTime {
9647 year: Some(year),
9648 month: Some(month),
9649 day: Some(day),
9650 ..Default::default()
9651 });
9652 }
9653
9654 #[cfg(feature = "timesync")]
9655 pub fn set_local_network_time_hm(&self, hour: u8, minute: u8) {
9657 self.set_local_network_time(PartialNetworkTime {
9658 hour: Some(hour),
9659 minute: Some(minute),
9660 ..Default::default()
9661 });
9662 }
9663
9664 #[cfg(feature = "timesync")]
9665 pub fn set_local_network_time_hms(&self, hour: u8, minute: u8, second: u8) {
9667 self.set_local_network_time(PartialNetworkTime {
9668 hour: Some(hour),
9669 minute: Some(minute),
9670 second: Some(second),
9671 ..Default::default()
9672 });
9673 }
9674
9675 #[cfg(feature = "timesync")]
9676 pub fn set_local_network_time_hms_millis(
9678 &self,
9679 hour: u8,
9680 minute: u8,
9681 second: u8,
9682 millisecond: u16,
9683 ) {
9684 self.set_local_network_time(PartialNetworkTime {
9685 hour: Some(hour),
9686 minute: Some(minute),
9687 second: Some(second),
9688 nanosecond: Some((millisecond as u32).saturating_mul(1_000_000)),
9689 ..Default::default()
9690 });
9691 }
9692
9693 #[cfg(feature = "timesync")]
9694 pub fn set_local_network_time_hms_nanos(
9696 &self,
9697 hour: u8,
9698 minute: u8,
9699 second: u8,
9700 nanosecond: u32,
9701 ) {
9702 self.set_local_network_time(PartialNetworkTime {
9703 hour: Some(hour),
9704 minute: Some(minute),
9705 second: Some(second),
9706 nanosecond: Some(nanosecond),
9707 ..Default::default()
9708 });
9709 }
9710
9711 #[cfg(feature = "timesync")]
9712 pub fn set_local_network_datetime(
9714 &self,
9715 year: i32,
9716 month: u8,
9717 day: u8,
9718 hour: u8,
9719 minute: u8,
9720 second: u8,
9721 ) {
9722 self.set_local_network_time(PartialNetworkTime {
9723 year: Some(year),
9724 month: Some(month),
9725 day: Some(day),
9726 hour: Some(hour),
9727 minute: Some(minute),
9728 second: Some(second),
9729 ..Default::default()
9730 });
9731 }
9732
9733 #[cfg(feature = "timesync")]
9734 #[allow(clippy::too_many_arguments)]
9735 pub fn set_local_network_datetime_millis(
9737 &self,
9738 year: i32,
9739 month: u8,
9740 day: u8,
9741 hour: u8,
9742 minute: u8,
9743 second: u8,
9744 millisecond: u16,
9745 ) {
9746 self.set_local_network_time(PartialNetworkTime {
9747 year: Some(year),
9748 month: Some(month),
9749 day: Some(day),
9750 hour: Some(hour),
9751 minute: Some(minute),
9752 second: Some(second),
9753 nanosecond: Some((millisecond as u32).saturating_mul(1_000_000)),
9754 });
9755 }
9756
9757 #[cfg(feature = "timesync")]
9758 #[allow(clippy::too_many_arguments)]
9759 pub fn set_local_network_datetime_nanos(
9761 &self,
9762 year: i32,
9763 month: u8,
9764 day: u8,
9765 hour: u8,
9766 minute: u8,
9767 second: u8,
9768 nanosecond: u32,
9769 ) {
9770 self.set_local_network_time(PartialNetworkTime {
9771 year: Some(year),
9772 month: Some(month),
9773 day: Some(day),
9774 hour: Some(hour),
9775 minute: Some(minute),
9776 second: Some(second),
9777 nanosecond: Some(nanosecond),
9778 });
9779 }
9780
9781 #[cfg(feature = "timesync")]
9782 pub fn clear_network_time_source(&self, source: &str) {
9784 let mut st = self.timesync.lock();
9785 st.clock.remove_source(source);
9786 }
9787
9788 #[cfg(feature = "timesync")]
9789 pub fn set_timesync_config(&self, cfg: Option<TimeSyncConfig>) {
9791 let mut st = self.timesync.lock();
9792 let stale_remote_sources: Vec<String> = st.remote_sources.keys().cloned().collect();
9793 st.cfg = cfg;
9794 st.tracker = cfg.map(TimeSyncTracker::new);
9795 st.disciplined_clock = SlewedNetworkClock::new(
9796 cfg.map(|c| c.max_slew_ppm)
9797 .unwrap_or(TimeSyncConfig::default().max_slew_ppm),
9798 );
9799 st.remote_sources.clear();
9800 st.next_seq = 1;
9801 st.next_announce_mono_ms = 0;
9802 st.next_request_mono_ms = 0;
9803 st.pending_request = None;
9804 st.clock.remove_source(INTERNAL_TIMESYNC_SOURCE_ID);
9805 for source in stale_remote_sources {
9806 st.clock.remove_source(&source);
9807 }
9808 st.clock.remove_source(LOCAL_TIMESYNC_FULL_SOURCE_ID);
9809 st.clock.remove_source(LOCAL_TIMESYNC_DATE_SOURCE_ID);
9810 st.clock.remove_source(LOCAL_TIMESYNC_TOD_SOURCE_ID);
9811 st.clock.remove_source(LOCAL_TIMESYNC_SUBSEC_SOURCE_ID);
9812 }
9813
9814 #[cfg(feature = "timesync")]
9815 pub fn network_time(&self) -> Option<NetworkTimeReading> {
9817 let now_ms = self.monotonic_now_ms();
9818 let now_ns = self.monotonic_now_ns();
9819 self.refresh_timesync_state(now_ms);
9820 let st = self.timesync.lock();
9821 if let Some(unix_ms) = st.disciplined_clock.read_unix_ms(now_ns) {
9822 return Some(NetworkTimeReading {
9823 time: PartialNetworkTime::from_unix_ms(unix_ms),
9824 unix_time_ms: Some(unix_ms),
9825 });
9826 }
9827 st.clock.current_time(now_ns)
9828 }
9829
9830 #[cfg(feature = "timesync")]
9831 pub fn network_time_ms(&self) -> Option<u64> {
9833 self.network_time().and_then(|t| t.unix_time_ms)
9834 }
9835
9836 #[cfg(feature = "timesync")]
9837 fn packet_timestamp_ms(&self) -> u64 {
9838 self.network_time_ms()
9839 .unwrap_or_else(|| self.monotonic_now_ms())
9840 }
9841
9842 #[cfg(not(feature = "timesync"))]
9843 fn packet_timestamp_ms(&self) -> u64 {
9844 self.clock.now_ms()
9845 }
9846
9847 #[cfg(feature = "timesync")]
9848 fn queue_internal_timesync_request(
9849 &self,
9850 seq: u64,
9851 t1_mono_ms: u64,
9852 called_from_queue: bool,
9853 ) -> TelemetryResult<()> {
9854 let pkt_ts = self.packet_timestamp_ms();
9855 if called_from_queue {
9856 self.log_queue_ts(DataType::TimeSyncRequest, pkt_ts, &[seq, t1_mono_ms])
9857 } else {
9858 self.log_ts(DataType::TimeSyncRequest, pkt_ts, &[seq, t1_mono_ms])
9859 }
9860 }
9861
9862 #[cfg(feature = "timesync")]
9863 fn queue_internal_timesync_response(
9864 &self,
9865 seq: u64,
9866 t1_mono_ms: u64,
9867 t2_network_ms: u64,
9868 t3_network_ms: u64,
9869 dst: Option<RouterSideId>,
9870 called_from_queue: bool,
9871 ) -> TelemetryResult<()> {
9872 let pkt_ts = self.packet_timestamp_ms();
9873 let payload = encode_slice_le(&[seq, t1_mono_ms, t2_network_ms, t3_network_ms]);
9874 let sender = self.sender_arc();
9875 let pkt = Packet::new(
9876 DataType::TimeSyncResponse,
9877 &[DataEndpoint::TimeSync],
9878 sender.as_ref(),
9879 pkt_ts,
9880 payload,
9881 )?;
9882 match dst {
9883 Some(dst) => self.emit_internal_tx(
9884 RouterTxItem::ToSide {
9885 src: None,
9886 dst,
9887 data: RouterItem::Packet(pkt),
9888 },
9889 true,
9890 called_from_queue,
9891 ),
9892 None => self.emit_internal_tx(
9893 RouterTxItem::Broadcast(RouterItem::Packet(pkt)),
9894 true,
9895 called_from_queue,
9896 ),
9897 }
9898 }
9899
9900 #[cfg(feature = "timesync")]
9901 pub fn poll_timesync(&self) -> TelemetryResult<bool> {
9903 let now_ms = self.monotonic_now_ms();
9904 let now_ns = self.monotonic_now_ns();
9905 let mut queued_any = false;
9906 let mut announce_priority = None;
9907 let mut request = None;
9908
9909 {
9910 let mut st = self.timesync.lock();
9911 st.clock.prune_expired(now_ms);
9912 let timeout_ms = st.cfg.map(|cfg| cfg.source_timeout_ms).unwrap_or(0);
9913 st.remote_sources
9914 .retain(|_, src| now_ms.saturating_sub(src.last_sample_mono_ms) <= timeout_ms);
9915 let Some(cfg) = st.cfg else {
9916 return Ok(false);
9917 };
9918
9919 let has_usable_time = Self::timesync_has_usable_time_locked(&st, now_ns);
9920 let (leader, announce_prio) = if let Some(tracker) = st.tracker.as_mut() {
9921 let _ = tracker.refresh(now_ms);
9922 (
9923 tracker.leader(now_ms, has_usable_time),
9924 tracker.local_announce_priority(now_ms, has_usable_time),
9925 )
9926 } else {
9927 (None, None)
9928 };
9929 Self::reconcile_pending_timesync_request_locked(&mut st, &leader, now_ms);
9930
9931 if let Some(TimeSyncLeader::Remote(remote)) = leader.as_ref() {
9932 let target_ms = st
9933 .remote_sources
9934 .get(&remote.sender)
9935 .map(|src| src.sample_unix_ms);
9936 if let Some(target_ms) = target_ms {
9937 st.disciplined_clock.steer_unix_ms(now_ns, target_ms);
9938 }
9939 }
9940
9941 if let Some(priority) = announce_prio
9942 && now_ms >= st.next_announce_mono_ms
9943 {
9944 announce_priority = Some(priority);
9945 st.next_announce_mono_ms = now_ms.saturating_add(cfg.announce_interval_ms);
9946 }
9947
9948 if let Some(TimeSyncLeader::Remote(remote)) = leader
9949 && now_ms >= st.next_request_mono_ms
9950 && st.pending_request.is_none()
9951 {
9952 let seq = st.next_seq;
9953 let next = st.next_seq.wrapping_add(1);
9954 st.next_seq = if next == 0 { 1 } else { next };
9955 st.next_request_mono_ms = now_ms.saturating_add(cfg.request_interval_ms);
9956 st.pending_request = Some(PendingTimeSyncRequest {
9957 seq,
9958 t1_mono_ms: now_ms,
9959 source: remote.sender,
9960 });
9961 request = Some((seq, now_ms));
9962 }
9963 }
9964
9965 if let Some(priority) = announce_priority {
9966 let time_ms = self.packet_timestamp_ms();
9967 self.log_queue_ts(DataType::TimeSyncAnnounce, time_ms, &[priority, time_ms])?;
9968 queued_any = true;
9969 }
9970 if let Some((seq, t1_mono_ms)) = request {
9971 self.queue_internal_timesync_request(seq, t1_mono_ms, true)?;
9972 queued_any = true;
9973 }
9974
9975 Ok(queued_any)
9976 }
9977
9978 #[cfg(feature = "timesync")]
9979 fn handle_internal_timesync_packet(
9980 &self,
9981 pkt: &Packet,
9982 src: Option<RouterSideId>,
9983 called_from_queue: bool,
9984 ) -> TelemetryResult<bool> {
9985 let Some(cfg) = self.cfg.timesync_config() else {
9986 if self.should_route_remote(&RouterItem::Packet(pkt.clone()), src)? {
9987 self.relay_send(RouterItem::Packet(pkt.clone()), src, called_from_queue)?;
9988 }
9989 return Ok(true);
9990 };
9991
9992 let now_mono_ms = self.monotonic_now_ms();
9993 let now_mono_ns = self.monotonic_now_ns();
9994 let mut response = None;
9995 let mut poll_after = false;
9996
9997 {
9998 let mut st = self.timesync.lock();
9999 st.clock.prune_expired(now_mono_ms);
10000 let timeout_ms = st.cfg.map(|cfg| cfg.source_timeout_ms).unwrap_or(0);
10001 st.remote_sources
10002 .retain(|_, src| now_mono_ms.saturating_sub(src.last_sample_mono_ms) <= timeout_ms);
10003 let has_usable_time = Self::timesync_has_usable_time_locked(&st, now_mono_ns);
10004 if st.tracker.is_none() {
10005 return Ok(true);
10006 }
10007
10008 match pkt.data_type() {
10009 DataType::TimeSyncAnnounce => {
10010 let ann = decode_timesync_announce(pkt)?;
10011 let should_steer = {
10012 let tracker = st.tracker.as_mut().expect("tracker checked above");
10013 let _ = tracker.handle_announce(pkt, now_mono_ms)?;
10014 matches!(
10015 tracker.leader(now_mono_ms, has_usable_time),
10016 Some(TimeSyncLeader::Remote(ref remote)) if remote.sender == pkt.sender()
10017 )
10018 };
10019 st.remote_sources.insert(
10020 pkt.sender().to_owned(),
10021 RemoteTimeSyncSource {
10022 priority: ann.priority,
10023 last_sample_mono_ms: now_mono_ms,
10024 sample_unix_ms: ann.time_ms,
10025 },
10026 );
10027 st.clock.update_source(
10028 pkt.sender(),
10029 ann.priority,
10030 PartialNetworkTime::from_unix_ms(ann.time_ms),
10031 now_mono_ms,
10032 now_mono_ns,
10033 Some(cfg.source_timeout_ms),
10034 );
10035 if should_steer {
10036 st.disciplined_clock.steer_unix_ms(now_mono_ns, ann.time_ms);
10037 }
10038 poll_after = true;
10039 }
10040 DataType::TimeSyncRequest => {
10041 let should_serve = {
10042 let tracker = st.tracker.as_ref().expect("tracker checked above");
10043 tracker.should_serve(now_mono_ms, has_usable_time)
10044 };
10045 if should_serve {
10046 let req = decode_timesync_request(pkt)?;
10047 let network_now = st
10048 .disciplined_clock
10049 .read_unix_ms(now_mono_ns)
10050 .or_else(|| {
10051 st.clock
10052 .current_time(now_mono_ns)
10053 .and_then(|t| t.unix_time_ms)
10054 })
10055 .unwrap_or(now_mono_ms);
10056 let t2 = network_now;
10057 let t3 = network_now;
10058 response = Some((req.seq, req.t1_ms, t2, t3, src));
10059 }
10060 }
10061 DataType::TimeSyncResponse => {
10062 let resp = decode_timesync_response(pkt)?;
10063 let pending = st.pending_request.clone();
10064 if let Some(pending) = pending
10065 && pending.seq == resp.seq
10066 && pending.source == pkt.sender()
10067 {
10068 let source_priority = {
10069 let tracker = st.tracker.as_ref().expect("tracker checked above");
10070 tracker
10071 .best_active_source(now_mono_ms)
10072 .map(|s| s.priority)
10073 .or_else(|| st.remote_sources.get(pkt.sender()).map(|s| s.priority))
10074 .unwrap_or(cfg.priority)
10075 };
10076 let (estimate_ms, _delay_ms) = compute_network_time_sample(
10077 pending.t1_mono_ms,
10078 resp.t2_ms,
10079 resp.t3_ms,
10080 now_mono_ms,
10081 );
10082 st.remote_sources.insert(
10083 pkt.sender().to_owned(),
10084 RemoteTimeSyncSource {
10085 priority: source_priority,
10086 last_sample_mono_ms: now_mono_ms,
10087 sample_unix_ms: estimate_ms,
10088 },
10089 );
10090 st.clock.update_source(
10091 pkt.sender(),
10092 source_priority,
10093 PartialNetworkTime::from_unix_ms(estimate_ms),
10094 now_mono_ms,
10095 now_mono_ns,
10096 Some(cfg.source_timeout_ms),
10097 );
10098 st.disciplined_clock.steer_unix_ms(now_mono_ns, estimate_ms);
10099 st.pending_request = None;
10100 }
10101 }
10102 _ => {}
10103 }
10104 }
10105
10106 if let Some((seq, t1, t2, t3, dst)) = response {
10107 self.queue_internal_timesync_response(seq, t1, t2, t3, dst, called_from_queue)?;
10108 }
10109 if poll_after {
10110 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
10111 }
10112
10113 if self.should_route_remote(&RouterItem::Packet(pkt.clone()), src)? {
10114 self.relay_send(RouterItem::Packet(pkt.clone()), src, called_from_queue)?;
10115 }
10116
10117 Ok(true)
10118 }
10119
10120 #[cfg(feature = "std")]
10122 pub fn new(cfg: RouterConfig) -> Self {
10123 Self::new_with_clock(cfg, Box::new(StdMonotonicClock::default()))
10124 }
10125
10126 pub fn new_with_clock(cfg: RouterConfig, clock: Box<dyn Clock + Send + Sync>) -> Self {
10128 #[cfg(feature = "timesync")]
10129 let timesync_cfg = cfg.timesync_config();
10130 #[cfg(feature = "discovery")]
10131 let preferred_discovery_master = cfg.preferred_discovery_master.clone();
10132 let memory = cfg.memory_config();
10133 let hostname: Arc<str> = Arc::from(cfg.sender());
10134 let address_mode = cfg.address_mode();
10135 let instance_seq = u64::from(ROUTER_INSTANCE_SEQ.fetch_add(1, Ordering::Relaxed));
10136 let owner_hash = Self::sender_hash(hostname.as_ref()) ^ instance_seq;
10137 let fallback = Self::fallback_address_for_hostname(hostname.as_ref());
10138 let requested = address_mode.requested_address();
10139 let address = if requested == 0 { fallback } else { requested };
10140 let local_address = AddressBookEntry {
10141 hostname: hostname.clone(),
10142 address,
10143 requested_address: requested,
10144 mode: address_mode,
10145 birth_ms: clock
10146 .now_ms()
10147 .saturating_mul(1_000_000)
10148 .saturating_add(instance_seq),
10149 owner_hash,
10150 last_seen_ms: clock.now_ms(),
10151 };
10152 let mut address_book = BTreeMap::new();
10153 address_book.insert(hostname.to_string(), local_address.clone());
10154 let mut address_by_value = BTreeMap::new();
10155 address_by_value.insert(address, hostname.to_string());
10156 Self {
10157 sender: RouterMutex::new(hostname),
10158 cfg,
10159 state: RouterMutex::new(RouterInner {
10160 memory,
10161 sides: Vec::new(),
10162 route_overrides: BTreeMap::new(),
10163 typed_route_overrides: BTreeMap::new(),
10164 route_weights: BTreeMap::new(),
10165 route_priorities: BTreeMap::new(),
10166 source_route_modes: BTreeMap::new(),
10167 route_selection_cursors: BTreeMap::new(),
10168 adaptive_route_stats: BTreeMap::new(),
10169 side_runtime_stats: BTreeMap::new(),
10170 side_transport: BTreeMap::new(),
10171 managed_variable_types: BTreeSet::new(),
10172 managed_variable_permissions: BTreeMap::new(),
10173 managed_variable_latest: BTreeMap::new(),
10174 network_variable_update_handlers: BTreeMap::new(),
10175 local_address,
10176 address_book,
10177 address_by_value,
10178 p2p_port_handlers: BTreeMap::new(),
10179 p2p_stream_handlers: BTreeMap::new(),
10180 p2p_stream_sessions: BTreeMap::new(),
10181 next_p2p_stream_id: 1,
10182 received_queue: BoundedDeque::new(
10183 memory.max_queue_budget,
10184 memory.starting_queue_size,
10185 memory.queue_grow_step,
10186 ),
10187 transmit_queue: BoundedDeque::new(
10188 memory.max_queue_budget,
10189 memory.starting_queue_size,
10190 memory.queue_grow_step,
10191 ),
10192 tx_priority_burst: 0,
10193 recent_rx: BoundedDeque::new(
10194 memory.recent_rx_queue_bytes(),
10195 memory.recent_rx_queue_bytes(),
10196 memory.queue_grow_step,
10197 ),
10198 reliable_tx: BTreeMap::new(),
10199 reliable_rx: BTreeMap::new(),
10200 reliable_return_routes: BTreeMap::new(),
10201 reliable_return_route_order: VecDeque::new(),
10202 end_to_end_reliable_tx: BTreeMap::new(),
10203 end_to_end_reliable_tx_order: VecDeque::new(),
10204 total_handler_failures: 0,
10205 total_handler_retries: 0,
10206 #[cfg(feature = "discovery")]
10207 discovery_routes: BTreeMap::new(),
10208 #[cfg(feature = "discovery")]
10209 discovery_cadence: DiscoveryCadenceState::default(),
10210 #[cfg(feature = "discovery")]
10211 discovery_side_throttle: BTreeMap::new(),
10212 #[cfg(feature = "discovery")]
10213 preferred_discovery_master,
10214 #[cfg(all(feature = "discovery", feature = "timesync"))]
10215 timesync_side_throttle: BTreeMap::new(),
10216 }),
10217 isr_rx_queue: IsrRxQueue::new(
10218 memory.max_queue_budget,
10219 memory.starting_queue_size,
10220 memory.queue_grow_step,
10221 ),
10222 side_tx_gate: ReentryGate::new(),
10223 clock,
10224 #[cfg(feature = "timesync")]
10225 timesync: RouterMutex::new(TimeSyncRuntime::new(timesync_cfg)),
10226 }
10227 }
10228
10229 #[inline]
10230 fn sender_arc(&self) -> Arc<str> {
10231 self.sender.lock().clone()
10232 }
10233
10234 #[inline]
10235 pub fn sender(&self) -> Arc<str> {
10236 self.sender_arc()
10237 }
10238
10239 pub fn current_address(&self) -> NodeAddress {
10240 self.state.lock().local_address.address
10241 }
10242
10243 pub fn hostname(&self) -> Arc<str> {
10244 self.sender_arc()
10245 }
10246
10247 pub fn set_preferred_discovery_master(&self, hostname: Option<&str>) {
10252 #[cfg(feature = "discovery")]
10253 {
10254 let preferred = hostname
10255 .filter(|value| !value.is_empty())
10256 .map(Arc::<str>::from);
10257 let mut st = self.state.lock();
10258 if st.preferred_discovery_master != preferred {
10259 st.preferred_discovery_master = preferred;
10260 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10261 }
10262 }
10263 #[cfg(not(feature = "discovery"))]
10264 let _ = hostname;
10265 }
10266
10267 pub fn preferred_discovery_master(&self) -> Option<Arc<str>> {
10269 #[cfg(feature = "discovery")]
10270 {
10271 return self.state.lock().preferred_discovery_master.clone();
10272 }
10273 #[cfg(not(feature = "discovery"))]
10274 None
10275 }
10276
10277 pub fn address_book(&self) -> Vec<AddressBookEntry> {
10278 self.state.lock().address_book.values().cloned().collect()
10279 }
10280
10281 pub fn resolve_hostname(&self, hostname: &str) -> Option<AddressBookEntry> {
10282 let now_ms = self.clock.now_ms();
10283 #[allow(unused_mut)]
10284 let mut st = self.state.lock();
10285 #[cfg(feature = "discovery")]
10286 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
10287 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10288 }
10289 if let Some(entry) = st.address_book.get(hostname) {
10290 return Some(entry.clone());
10291 }
10292 let address = sender_address_u32(hostname);
10298 if Self::hostname_for_address_locked(&st, address).as_deref() != Some(hostname) {
10299 return None;
10300 }
10301 Some(AddressBookEntry {
10302 hostname: Arc::from(hostname),
10303 address,
10304 requested_address: 0,
10305 mode: AddressAssignmentMode::Dynamic,
10306 birth_ms: 0,
10307 owner_hash: Self::sender_hash(hostname),
10308 last_seen_ms: now_ms,
10309 })
10310 }
10311
10312 pub fn resolve_address(&self, address: NodeAddress) -> Option<AddressBookEntry> {
10313 let st = self.state.lock();
10314 let direct = st
10315 .address_by_value
10316 .get(&address)
10317 .and_then(|hostname| st.address_book.get(hostname))
10318 .cloned();
10319 if direct.is_some() {
10320 return direct;
10321 }
10322
10323 let hostname = Self::hostname_for_address_locked(&st, address)?;
10324 if let Some(entry) = st.address_book.get(&hostname) {
10325 return Some(entry.clone());
10326 }
10327
10328 let hostname: Arc<str> = Arc::from(hostname);
10333 Some(AddressBookEntry {
10334 hostname: hostname.clone(),
10335 address,
10336 requested_address: 0,
10337 mode: AddressAssignmentMode::Dynamic,
10338 birth_ms: 0,
10339 owner_hash: Self::sender_hash(hostname.as_ref()),
10340 last_seen_ms: self.clock.now_ms(),
10341 })
10342 }
10343
10344 pub fn bind_p2p_port<F>(&self, port: P2pPort, f: F) -> TelemetryResult<()>
10345 where
10346 F: Fn(P2pMessage<'_>) -> TelemetryResult<()> + Send + Sync + 'static,
10347 {
10348 if port == 0 {
10349 return Err(TelemetryError::BadArg);
10350 }
10351 let mut st = self.state.lock();
10352 st.p2p_port_handlers
10353 .entry(port)
10354 .or_default()
10355 .push(P2pPortHandler {
10356 handler: Arc::new(f),
10357 });
10358 Ok(())
10359 }
10360
10361 pub fn clear_p2p_port(&self, port: P2pPort) {
10362 self.state.lock().p2p_port_handlers.remove(&port);
10363 }
10364
10365 pub fn bind_p2p_stream_port<F>(&self, port: P2pPort, f: F) -> TelemetryResult<()>
10366 where
10367 F: Fn(P2pStreamEvent<'_>) -> TelemetryResult<()> + Send + Sync + 'static,
10368 {
10369 if port == 0 {
10370 return Err(TelemetryError::BadArg);
10371 }
10372 let mut st = self.state.lock();
10373 st.p2p_stream_handlers
10374 .entry(port)
10375 .or_default()
10376 .push(P2pStreamHandler {
10377 handler: Arc::new(f),
10378 });
10379 Ok(())
10380 }
10381
10382 pub fn clear_p2p_stream_port(&self, port: P2pPort) {
10383 self.state.lock().p2p_stream_handlers.remove(&port);
10384 }
10385
10386 fn send_p2p_to_entry(
10387 &self,
10388 dst: AddressBookEntry,
10389 dst_port: P2pPort,
10390 src_port: P2pPort,
10391 payload: &[u8],
10392 ) -> TelemetryResult<()> {
10393 if dst_port == 0 {
10394 return Err(TelemetryError::BadArg);
10395 }
10396 let local = self.state.lock().local_address.clone();
10397 let payload = Self::encode_p2p_payload(
10398 local.hostname.as_ref(),
10399 local.address,
10400 src_port,
10401 dst_port,
10402 payload,
10403 )?;
10404 let pkt = Packet::new(
10405 DataType::P2pMessage,
10406 &[DataEndpoint::Discovery],
10407 local.hostname.as_ref(),
10408 self.packet_timestamp_ms(),
10409 crate::shared_bytes::convert(payload),
10410 )?;
10411 let target = Self::sender_hash(dst.hostname.as_ref());
10412 let item = self.attach_wire_contract_to_item(RouterItem::Packet(pkt.clone()), &[target])?;
10413 if target == Self::sender_hash(self.sender_arc().as_ref()) {
10414 self.dispatch_p2p_packet(&pkt)?;
10415 return Ok(());
10416 }
10417 self.tx_item_impl(RouterTxItem::Broadcast(item), true, false)
10418 }
10419
10420 pub fn send_p2p_to_hostname(
10421 &self,
10422 hostname: &str,
10423 dst_port: P2pPort,
10424 src_port: P2pPort,
10425 payload: &[u8],
10426 ) -> TelemetryResult<()> {
10427 let dst = self
10428 .resolve_hostname(hostname)
10429 .ok_or(TelemetryError::BadArg)?;
10430 self.send_p2p_to_entry(dst, dst_port, src_port, payload)
10431 }
10432
10433 pub fn send_p2p_to_address(
10434 &self,
10435 address: NodeAddress,
10436 dst_port: P2pPort,
10437 src_port: P2pPort,
10438 payload: &[u8],
10439 ) -> TelemetryResult<()> {
10440 let dst = self
10441 .resolve_address(address)
10442 .ok_or(TelemetryError::BadArg)?;
10443 self.send_p2p_to_entry(dst, dst_port, src_port, payload)
10444 }
10445
10446 fn open_p2p_stream_to_entry(
10447 &self,
10448 dst: AddressBookEntry,
10449 dst_port: P2pPort,
10450 src_port: P2pPort,
10451 ) -> TelemetryResult<P2pStreamId> {
10452 if dst_port == 0 || src_port == 0 {
10453 return Err(TelemetryError::BadArg);
10454 }
10455 let stream_id = {
10456 let mut st = self.state.lock();
10457 let stream_id = Self::allocate_p2p_stream_id_locked(&mut st);
10458 if stream_id == 0 {
10459 return Err(TelemetryError::Io("p2p stream id exhausted"));
10460 }
10461 st.p2p_stream_sessions.insert(
10462 stream_id,
10463 P2pStreamSession {
10464 peer_hostname: dst.hostname.clone(),
10465 peer_address: dst.address,
10466 local_port: src_port,
10467 peer_port: dst_port,
10468 peer_stream_id: 0,
10469 next_sequence: 1,
10470 connected: false,
10471 },
10472 );
10473 stream_id
10474 };
10475 let payload = Self::encode_p2p_stream_payload(P2P_STREAM_SYN, stream_id, 0, 0, &[])?;
10476 self.send_p2p_to_entry(dst, dst_port, src_port, &payload)?;
10477 Ok(stream_id)
10478 }
10479
10480 pub fn open_p2p_stream_to_hostname(
10481 &self,
10482 hostname: &str,
10483 dst_port: P2pPort,
10484 src_port: P2pPort,
10485 ) -> TelemetryResult<P2pStreamId> {
10486 let dst = self
10487 .resolve_hostname(hostname)
10488 .ok_or(TelemetryError::BadArg)?;
10489 self.open_p2p_stream_to_entry(dst, dst_port, src_port)
10490 }
10491
10492 pub fn open_p2p_stream_to_address(
10493 &self,
10494 address: NodeAddress,
10495 dst_port: P2pPort,
10496 src_port: P2pPort,
10497 ) -> TelemetryResult<P2pStreamId> {
10498 let dst = self
10499 .resolve_address(address)
10500 .ok_or(TelemetryError::BadArg)?;
10501 self.open_p2p_stream_to_entry(dst, dst_port, src_port)
10502 }
10503
10504 fn send_p2p_stream_control(
10505 &self,
10506 stream_id: P2pStreamId,
10507 flags: u8,
10508 payload: &[u8],
10509 ) -> TelemetryResult<()> {
10510 let (dst, dst_port, src_port, peer_stream_id, seq, refresh_syn) = {
10511 let mut st = self.state.lock();
10512 let Some(session) = st.p2p_stream_sessions.get_mut(&stream_id) else {
10513 return Err(TelemetryError::BadArg);
10514 };
10515 let peer_hostname = session.peer_hostname.clone();
10516 let peer_address = session.peer_address;
10517 let peer_port = session.peer_port;
10518 let local_port = session.local_port;
10519 let peer_stream_id = session.peer_stream_id;
10520 let refresh_syn = peer_stream_id == 0 && flags & P2P_STREAM_SYN == 0;
10521 let seq = session.next_sequence;
10522 session.next_sequence = session.next_sequence.wrapping_add(1).max(1);
10523 let dst = st
10524 .address_book
10525 .get(peer_hostname.as_ref())
10526 .cloned()
10527 .unwrap_or(AddressBookEntry {
10528 hostname: peer_hostname.clone(),
10529 address: peer_address,
10530 requested_address: peer_address,
10531 mode: AddressAssignmentMode::Dynamic,
10532 birth_ms: self.clock.now_ms(),
10533 owner_hash: Self::sender_hash(peer_hostname.as_ref()),
10534 last_seen_ms: self.clock.now_ms(),
10535 });
10536 (dst, peer_port, local_port, peer_stream_id, seq, refresh_syn)
10537 };
10538 if refresh_syn {
10539 let syn_payload =
10540 Self::encode_p2p_stream_payload(P2P_STREAM_SYN, stream_id, 0, 0, &[])?;
10541 self.send_p2p_to_entry(dst.clone(), dst_port, src_port, &syn_payload)?;
10542 }
10543 let stream_payload =
10544 Self::encode_p2p_stream_payload(flags, stream_id, peer_stream_id, seq, payload)?;
10545 self.send_p2p_to_entry(dst, dst_port, src_port, &stream_payload)?;
10546 if flags & (P2P_STREAM_FIN | P2P_STREAM_RST) != 0 {
10547 self.state.lock().p2p_stream_sessions.remove(&stream_id);
10548 }
10549 Ok(())
10550 }
10551
10552 pub fn send_p2p_stream(&self, stream_id: P2pStreamId, payload: &[u8]) -> TelemetryResult<()> {
10553 self.send_p2p_stream_control(stream_id, P2P_STREAM_DATA, payload)
10554 }
10555
10556 pub fn close_p2p_stream(&self, stream_id: P2pStreamId) -> TelemetryResult<()> {
10557 self.send_p2p_stream_control(stream_id, P2P_STREAM_FIN, &[])
10558 }
10559
10560 pub fn reset_p2p_stream(&self, stream_id: P2pStreamId) -> TelemetryResult<()> {
10561 self.send_p2p_stream_control(stream_id, P2P_STREAM_RST, &[])
10562 }
10563
10564 pub fn set_sender<S: AsRef<str>>(&self, sender: S) {
10565 let hostname: Arc<str> = Arc::from(sender.as_ref());
10566 let mut st = self.state.lock();
10567 let mut local = st.local_address.clone();
10568 local.hostname = hostname.clone();
10569 local.owner_hash = Self::sender_hash(hostname.as_ref());
10570 if matches!(local.mode, AddressAssignmentMode::Dynamic) {
10571 local.address = Self::fallback_address_for_hostname(hostname.as_ref());
10572 local.requested_address = 0;
10573 }
10574 let change =
10575 self.update_local_identity_locked(&mut st, local, AddressChangeReason::Configured);
10576 drop(st);
10577 let _ = self.notify_address_change(change);
10578 }
10579
10580 pub fn set_address_assignment(
10581 &self,
10582 mode: AddressAssignmentMode,
10583 ) -> TelemetryResult<AddressChange> {
10584 let mut st = self.state.lock();
10585 let mut local = st.local_address.clone();
10586 local.mode = mode;
10587 local.requested_address = mode.requested_address();
10588 local.address = match mode {
10589 AddressAssignmentMode::Dynamic => {
10590 Self::fallback_address_for_hostname(local.hostname.as_ref())
10591 }
10592 AddressAssignmentMode::Requested(address) | AddressAssignmentMode::Static(address) => {
10593 if address == 0 {
10594 1
10595 } else {
10596 address
10597 }
10598 }
10599 };
10600 let change =
10601 self.update_local_identity_locked(&mut st, local, AddressChangeReason::Configured);
10602 drop(st);
10603 self.notify_address_change(change.clone())?;
10604 Ok(change)
10605 }
10606
10607 pub fn add_side_packed<N, F>(&self, name: N, tx: F) -> RouterSideId
10616 where
10617 N: AsRef<str>,
10618 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
10619 {
10620 self.add_side_packed_with_options(name, tx, RouterSideOptions::default())
10621 }
10622
10623 pub fn add_side_packed_small_packets<N, F>(
10627 &self,
10628 name: N,
10629 tx: F,
10630 max_frame_bytes: usize,
10631 ) -> RouterSideId
10632 where
10633 N: AsRef<str>,
10634 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
10635 {
10636 self.add_side_packed_with_options(
10637 name,
10638 tx,
10639 RouterSideOptions::default().with_small_packet_transport(max_frame_bytes),
10640 )
10641 }
10642
10643 pub fn add_side_packed_with_options<N, F>(
10652 &self,
10653 name: N,
10654 tx: F,
10655 opts: RouterSideOptions,
10656 ) -> RouterSideId
10657 where
10658 N: AsRef<str>,
10659 F: Fn(&[u8]) -> TelemetryResult<()> + Send + Sync + 'static,
10660 {
10661 let mut st = self.state.lock();
10662 let side = Some(RouterSide {
10663 name: Arc::from(name.as_ref()),
10664 tx_handler: RouterTxHandlerFn::Packed(Arc::new(tx)),
10665 opts,
10666 });
10667 let id = if let Some(id) = st.sides.iter().position(Option::is_none) {
10668 st.sides[id] = side;
10669 id
10670 } else {
10671 let id = st.sides.len();
10672 st.sides.push(side);
10673 id
10674 };
10675 st.side_runtime_stats
10676 .insert(id, SideRuntimeStatsInner::default());
10677 st.side_transport.insert(id, SideTransportState::default());
10678 #[cfg(feature = "discovery")]
10679 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10680 id
10681 }
10682
10683 pub fn add_side_packed_with_priority_and_options<N, F>(
10692 &self,
10693 name: N,
10694 tx: F,
10695 opts: RouterSideOptions,
10696 ) -> RouterSideId
10697 where
10698 N: AsRef<str>,
10699 F: Fn(&[u8], u8) -> TelemetryResult<()> + Send + Sync + 'static,
10700 {
10701 let mut st = self.state.lock();
10702 let side = Some(RouterSide {
10703 name: Arc::from(name.as_ref()),
10704 tx_handler: RouterTxHandlerFn::PackedWithPriority(Arc::new(tx)),
10705 opts,
10706 });
10707 let id = if let Some(id) = st.sides.iter().position(Option::is_none) {
10708 st.sides[id] = side;
10709 id
10710 } else {
10711 let id = st.sides.len();
10712 st.sides.push(side);
10713 id
10714 };
10715 st.side_runtime_stats
10716 .insert(id, SideRuntimeStatsInner::default());
10717 st.side_transport.insert(id, SideTransportState::default());
10718 #[cfg(feature = "discovery")]
10719 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10720 id
10721 }
10722
10723 pub fn add_side_packet<N, F>(&self, name: N, tx: F) -> RouterSideId
10728 where
10729 N: AsRef<str>,
10730 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
10731 {
10732 self.add_side_packet_with_options(name, tx, RouterSideOptions::default())
10733 }
10734
10735 pub fn add_side_packet_with_options<N, F>(
10741 &self,
10742 name: N,
10743 tx: F,
10744 opts: RouterSideOptions,
10745 ) -> RouterSideId
10746 where
10747 N: AsRef<str>,
10748 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
10749 {
10750 let mut st = self.state.lock();
10751 let side = Some(RouterSide {
10752 name: Arc::from(name.as_ref()),
10753 tx_handler: RouterTxHandlerFn::Packet(Arc::new(tx)),
10754 opts,
10755 });
10756 let id = if let Some(id) = st.sides.iter().position(Option::is_none) {
10757 st.sides[id] = side;
10758 id
10759 } else {
10760 let id = st.sides.len();
10761 st.sides.push(side);
10762 id
10763 };
10764 st.side_runtime_stats
10765 .insert(id, SideRuntimeStatsInner::default());
10766 st.side_transport.insert(id, SideTransportState::default());
10767 #[cfg(feature = "discovery")]
10768 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
10769 id
10770 }
10771
10772 pub fn remove_side(&self, side: RouterSideId) -> TelemetryResult<()> {
10777 let now_ms = self.clock.now_ms();
10778 {
10779 let mut st = self.state.lock();
10780 let slot = st.sides.get_mut(side).ok_or(TelemetryError::BadArg)?;
10781 if slot.is_none() {
10782 return Err(TelemetryError::BadArg);
10783 }
10784 *slot = None;
10785 while st.sides.last().is_some_and(Option::is_none) {
10786 st.sides.pop();
10787 }
10788 st.route_overrides
10793 .retain(|(src_side, dst_side), _| *src_side != Some(side) && *dst_side != side);
10794 st.typed_route_overrides
10795 .retain(|(src_side, _, dst_side), _| *src_side != Some(side) && *dst_side != side);
10796 st.route_weights
10797 .retain(|(src_side, dst_side), _| *src_side != Some(side) && *dst_side != side);
10798 st.route_priorities
10799 .retain(|(src_side, dst_side), _| *src_side != Some(side) && *dst_side != side);
10800 st.source_route_modes.remove(&Some(side));
10801 st.route_selection_cursors.remove(&Some(side));
10802 st.adaptive_route_stats.remove(&side);
10803 #[cfg(feature = "discovery")]
10804 st.discovery_side_throttle.remove(&side);
10805 #[cfg(all(feature = "discovery", feature = "timesync"))]
10806 st.timesync_side_throttle.remove(&side);
10807 st.side_runtime_stats.remove(&side);
10808 st.side_transport.remove(&side);
10809 st.reliable_return_routes
10810 .retain(|_, route| route.side != side);
10811 st.transmit_queue.retain(
10812 |queued| {
10813 !matches!(&queued.item, RouterTxItem::ToSide { dst, .. } if *dst == side)
10814 && !matches!(&queued.item, RouterTxItem::ReliableReplay { dst, .. } if *dst == side)
10815 },
10816 );
10817 st.received_queue.retain(|queued| queued.src != Some(side));
10818 st.reliable_tx.retain(|(side_id, _), _| *side_id != side);
10819 st.reliable_rx.retain(|(side_id, _), _| *side_id != side);
10820 #[cfg(feature = "discovery")]
10821 {
10822 st.discovery_routes.remove(&side);
10823 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10824 }
10825 }
10826 let mut isr_rx = self.isr_rx_queue.try_lock()?;
10827 isr_rx.retain(|queued| queued.src != Some(side));
10828 Ok(())
10829 }
10830
10831 pub fn set_side_ingress_enabled(
10836 &self,
10837 side: RouterSideId,
10838 enabled: bool,
10839 ) -> TelemetryResult<()> {
10840 let now_ms = self.clock.now_ms();
10841 let mut st = self.state.lock();
10842 let side_ref = st
10843 .sides
10844 .get_mut(side)
10845 .and_then(|side| side.as_mut())
10846 .ok_or(TelemetryError::BadArg)?;
10847 side_ref.opts.ingress_enabled = enabled;
10848 #[cfg(feature = "discovery")]
10849 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10850 Ok(())
10851 }
10852
10853 pub fn set_side_egress_enabled(
10857 &self,
10858 side: RouterSideId,
10859 enabled: bool,
10860 ) -> TelemetryResult<()> {
10861 let now_ms = self.clock.now_ms();
10862 let mut st = self.state.lock();
10863 let side_ref = st
10864 .sides
10865 .get_mut(side)
10866 .and_then(|side| side.as_mut())
10867 .ok_or(TelemetryError::BadArg)?;
10868 side_ref.opts.egress_enabled = enabled;
10869 if !enabled {
10870 st.transmit_queue.retain(
10871 |queued| {
10872 !matches!(&queued.item, RouterTxItem::ToSide { dst, .. } if *dst == side)
10873 && !matches!(&queued.item, RouterTxItem::ReliableReplay { dst, .. } if *dst == side)
10874 },
10875 );
10876 }
10877 #[cfg(feature = "discovery")]
10878 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10879 Ok(())
10880 }
10881
10882 pub fn set_source_route_mode(
10888 &self,
10889 src: Option<RouterSideId>,
10890 mode: RouteSelectionMode,
10891 ) -> TelemetryResult<()> {
10892 let now_ms = self.clock.now_ms();
10893 let mut st = self.state.lock();
10894 if let Some(src) = src {
10895 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10896 }
10897 st.source_route_modes.insert(src, mode);
10901 st.route_selection_cursors.remove(&src);
10902 #[cfg(feature = "discovery")]
10903 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10904 Ok(())
10905 }
10906
10907 pub fn clear_source_route_mode(&self, src: Option<RouterSideId>) -> TelemetryResult<()> {
10909 let now_ms = self.clock.now_ms();
10910 let mut st = self.state.lock();
10911 if let Some(src) = src {
10912 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10913 }
10914 st.source_route_modes.remove(&src);
10915 st.route_selection_cursors.remove(&src);
10916 #[cfg(feature = "discovery")]
10917 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10918 Ok(())
10919 }
10920
10921 pub fn set_route_weight(
10926 &self,
10927 src: Option<RouterSideId>,
10928 dst: RouterSideId,
10929 weight: u32,
10930 ) -> TelemetryResult<()> {
10931 let now_ms = self.clock.now_ms();
10932 let mut st = self.state.lock();
10933 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10934 if let Some(src) = src {
10935 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10936 }
10937 st.route_weights.insert((src, dst), weight);
10938 st.route_selection_cursors.remove(&src);
10939 #[cfg(feature = "discovery")]
10940 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10941 Ok(())
10942 }
10943
10944 pub fn clear_route_weight(
10946 &self,
10947 src: Option<RouterSideId>,
10948 dst: RouterSideId,
10949 ) -> TelemetryResult<()> {
10950 let now_ms = self.clock.now_ms();
10951 let mut st = self.state.lock();
10952 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10953 if let Some(src) = src {
10954 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10955 }
10956 st.route_weights.remove(&(src, dst));
10957 st.route_selection_cursors.remove(&src);
10958 #[cfg(feature = "discovery")]
10959 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10960 Ok(())
10961 }
10962
10963 pub fn set_route_priority(
10968 &self,
10969 src: Option<RouterSideId>,
10970 dst: RouterSideId,
10971 priority: u32,
10972 ) -> TelemetryResult<()> {
10973 let now_ms = self.clock.now_ms();
10974 let mut st = self.state.lock();
10975 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10976 if let Some(src) = src {
10977 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10978 }
10979 st.route_priorities.insert((src, dst), priority);
10980 #[cfg(feature = "discovery")]
10981 Self::note_discovery_topology_change_locked(&mut st, now_ms);
10982 Ok(())
10983 }
10984
10985 pub fn clear_route_priority(
10987 &self,
10988 src: Option<RouterSideId>,
10989 dst: RouterSideId,
10990 ) -> TelemetryResult<()> {
10991 let now_ms = self.clock.now_ms();
10992 let mut st = self.state.lock();
10993 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
10994 if let Some(src) = src {
10995 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
10996 }
10997 st.route_priorities.remove(&(src, dst));
10998 #[cfg(feature = "discovery")]
10999 Self::note_discovery_topology_change_locked(&mut st, now_ms);
11000 Ok(())
11001 }
11002
11003 pub fn set_route(
11008 &self,
11009 src: Option<RouterSideId>,
11010 dst: RouterSideId,
11011 enabled: bool,
11012 ) -> TelemetryResult<()> {
11013 let now_ms = self.clock.now_ms();
11014 let mut st = self.state.lock();
11015 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
11016 if let Some(src) = src {
11017 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
11018 }
11019 st.route_overrides.insert((src, dst), enabled);
11020 #[cfg(feature = "discovery")]
11021 Self::note_discovery_topology_change_locked(&mut st, now_ms);
11022 Ok(())
11023 }
11024
11025 pub fn set_typed_route(
11030 &self,
11031 src: Option<RouterSideId>,
11032 ty: DataType,
11033 dst: RouterSideId,
11034 enabled: bool,
11035 ) -> TelemetryResult<()> {
11036 let now_ms = self.clock.now_ms();
11037 let mut st = self.state.lock();
11038 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
11039 if let Some(src) = src {
11040 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
11041 }
11042 st.typed_route_overrides
11043 .insert((src, ty.as_u32(), dst), enabled);
11044 #[cfg(feature = "discovery")]
11045 Self::note_discovery_topology_change_locked(&mut st, now_ms);
11046 Ok(())
11047 }
11048
11049 pub fn clear_typed_route(
11051 &self,
11052 src: Option<RouterSideId>,
11053 ty: DataType,
11054 dst: RouterSideId,
11055 ) -> TelemetryResult<()> {
11056 let now_ms = self.clock.now_ms();
11057 let mut st = self.state.lock();
11058 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
11059 if let Some(src) = src {
11060 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
11061 }
11062 st.typed_route_overrides.remove(&(src, ty.as_u32(), dst));
11063 #[cfg(feature = "discovery")]
11064 Self::note_discovery_topology_change_locked(&mut st, now_ms);
11065 Ok(())
11066 }
11067
11068 pub fn clear_route(&self, src: Option<RouterSideId>, dst: RouterSideId) -> TelemetryResult<()> {
11070 let now_ms = self.clock.now_ms();
11071 let mut st = self.state.lock();
11072 let _ = Self::side_ref(&st, dst).map_err(|_| TelemetryError::BadArg)?;
11073 if let Some(src) = src {
11074 let _ = Self::side_ref(&st, src).map_err(|_| TelemetryError::BadArg)?;
11075 }
11076 st.route_overrides.remove(&(src, dst));
11077 #[cfg(feature = "discovery")]
11078 Self::note_discovery_topology_change_locked(&mut st, now_ms);
11079 Ok(())
11080 }
11081
11082 #[cfg(feature = "discovery")]
11084 pub fn announce_discovery(&self) -> TelemetryResult<()> {
11085 self.queue_discovery_announce(true, false)
11086 }
11087
11088 #[cfg(feature = "discovery")]
11090 pub fn announce_leave(&self) -> TelemetryResult<()> {
11091 let sender = self.sender_arc();
11092 let pkt = discovery::build_discovery_leave(sender.as_ref(), self.clock.now_ms())?;
11093 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
11094 }
11095
11096 #[cfg(feature = "discovery")]
11098 pub fn poll_discovery(&self) -> TelemetryResult<bool> {
11099 self.poll_discovery_announce()
11100 }
11101
11102 #[cfg(feature = "discovery")]
11103 pub fn request_topology(&self) -> TelemetryResult<()> {
11104 let sender = self.sender_arc();
11105 let pkt =
11106 discovery::build_discovery_topology_request(sender.as_ref(), self.clock.now_ms())?;
11107 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
11108 }
11109
11110 #[cfg(feature = "discovery")]
11111 pub fn request_schema(&self) -> TelemetryResult<()> {
11112 let sender = self.sender_arc();
11113 let pkt = discovery::build_discovery_schema_request(sender.as_ref(), self.clock.now_ms())?;
11114 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
11115 }
11116
11117 pub fn enable_managed_variable(&self, ty: DataType) -> TelemetryResult<()> {
11124 self.enable_network_variable(ty, NetworkVariablePermissions::READ_WRITE)
11125 }
11126
11127 pub fn enable_network_variable(
11132 &self,
11133 ty: DataType,
11134 permissions: NetworkVariablePermissions,
11135 ) -> TelemetryResult<()> {
11136 if is_internal_control_type(ty) {
11137 return Err(TelemetryError::InvalidType);
11138 }
11139 let mut st = self.state.lock();
11140 let newly_advertised = st.managed_variable_types.insert(ty.as_u32());
11141 st.managed_variable_permissions
11142 .insert(ty.as_u32(), permissions);
11143 #[cfg(feature = "discovery")]
11144 if newly_advertised {
11145 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
11146 }
11147 Ok(())
11148 }
11149
11150 pub fn on_network_variable_update<F>(&self, ty: DataType, f: F) -> TelemetryResult<()>
11156 where
11157 F: Fn(&Packet) -> TelemetryResult<()> + Send + Sync + 'static,
11158 {
11159 if is_internal_control_type(ty) {
11160 return Err(TelemetryError::InvalidType);
11161 }
11162 let mut st = self.state.lock();
11163 let newly_advertised = st.managed_variable_types.insert(ty.as_u32());
11164 st.network_variable_update_handlers
11165 .entry(ty.as_u32())
11166 .or_default()
11167 .push(NetworkVariableUpdateHandler {
11168 handler: Arc::new(f),
11169 });
11170 #[cfg(feature = "discovery")]
11171 if newly_advertised {
11172 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
11173 }
11174 Ok(())
11175 }
11176
11177 pub fn disable_managed_variable(&self, ty: DataType) {
11178 let mut st = self.state.lock();
11179 let was_advertised = st.managed_variable_types.remove(&ty.as_u32());
11180 st.managed_variable_permissions.remove(&ty.as_u32());
11181 st.managed_variable_latest.remove(&ty.as_u32());
11182 st.network_variable_update_handlers.remove(&ty.as_u32());
11183 #[cfg(feature = "discovery")]
11184 if was_advertised {
11185 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
11186 }
11187 }
11188
11189 pub fn seed_managed_variable(&self, pkt: Packet) -> TelemetryResult<()> {
11190 if is_internal_control_type(pkt.data_type()) {
11191 return Err(TelemetryError::InvalidType);
11192 }
11193 pkt.validate()?;
11194 {
11195 let mut st = self.state.lock();
11196 let newly_advertised = st.managed_variable_types.insert(pkt.data_type().as_u32());
11197 #[cfg(feature = "discovery")]
11198 if newly_advertised {
11199 Self::note_discovery_topology_change_locked(&mut st, self.clock.now_ms());
11200 }
11201 }
11202 self.cache_managed_variable_packet(&pkt, false)
11203 }
11204
11205 pub fn cached_managed_variable(&self, ty: DataType) -> Option<Packet> {
11206 self.managed_variable_latest(ty)
11207 }
11208
11209 pub fn set_network_variable(&self, pkt: Packet) -> TelemetryResult<()> {
11215 if is_internal_control_type(pkt.data_type()) {
11216 return Err(TelemetryError::InvalidType);
11217 }
11218 pkt.validate()?;
11219 let ty = pkt.data_type();
11220 {
11221 let mut st = self.state.lock();
11222 st.managed_variable_types.insert(ty.as_u32());
11223 let perms = Self::managed_variable_permissions_locked(&st, ty);
11224 if !perms.write {
11225 return Err(TelemetryError::PermissionDenied);
11226 }
11227 }
11228 self.cache_managed_variable_packet(&pkt, false)?;
11229 #[cfg(feature = "discovery")]
11230 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
11231 let item = RouterTxItem::Broadcast(RouterItem::Packet(pkt));
11232 if self.side_tx_active() {
11233 return self.tx_queue_item_with_priority(
11234 item,
11235 false,
11236 crate::transport_priority(DataType::ManagedVariableValue),
11237 );
11238 }
11239 self.tx_item(item)
11240 }
11241
11242 #[cfg(feature = "discovery")]
11249 pub fn get_network_variable(
11250 &self,
11251 ty: DataType,
11252 stale_after_ms: Option<u64>,
11253 ) -> TelemetryResult<Option<Packet>> {
11254 if is_internal_control_type(ty) {
11255 return Err(TelemetryError::InvalidType);
11256 }
11257 {
11258 let mut st = self.state.lock();
11259 st.managed_variable_types.insert(ty.as_u32());
11260 let perms = Self::managed_variable_permissions_locked(&st, ty);
11261 if !perms.read {
11262 return Err(TelemetryError::PermissionDenied);
11263 }
11264 }
11265 let cached = self.managed_variable_latest_with_age(ty);
11266 let needs_refresh = match (cached.as_ref(), stale_after_ms) {
11267 (None, _) => true,
11268 (Some((_pkt, age_ms)), Some(max_age_ms)) => *age_ms > max_age_ms,
11269 (Some(_), None) => false,
11270 };
11271 if needs_refresh {
11272 self.request_managed_variable(ty)?;
11273 }
11274 Ok(cached.map(|(pkt, _age_ms)| pkt))
11275 }
11276
11277 pub fn get_cached_network_variable(&self, ty: DataType) -> TelemetryResult<Option<Packet>> {
11279 if is_internal_control_type(ty) {
11280 return Err(TelemetryError::InvalidType);
11281 }
11282 if !self.can_read_managed_variable(ty) {
11283 return Err(TelemetryError::PermissionDenied);
11284 }
11285 Ok(self.managed_variable_latest(ty))
11286 }
11287
11288 #[cfg(feature = "discovery")]
11289 pub fn request_managed_variable(&self, ty: DataType) -> TelemetryResult<()> {
11290 if is_internal_control_type(ty) {
11291 return Err(TelemetryError::InvalidType);
11292 }
11293 if !self.can_read_managed_variable(ty) {
11294 return Err(TelemetryError::PermissionDenied);
11295 }
11296 let sender = self.sender_arc();
11297 let pkt =
11298 discovery::build_managed_variable_request(sender.as_ref(), self.clock.now_ms(), ty)?;
11299 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
11300 }
11301
11302 #[cfg(feature = "discovery")]
11303 pub fn request_managed_variable_by_name(&self, ty_name: &str) -> TelemetryResult<()> {
11304 let ty = DataType::try_named(ty_name).ok_or(TelemetryError::InvalidType)?;
11305 self.request_managed_variable(ty)
11306 }
11307
11308 #[cfg(feature = "discovery")]
11310 pub fn export_topology(&self) -> TopologySnapshot {
11311 let now_ms = self.clock.now_ms();
11312 let mut st = self.state.lock();
11313 if Self::prune_discovery_routes_locked(&mut st, now_ms) {
11314 Self::note_discovery_topology_change_locked(&mut st, now_ms);
11315 }
11316 let routes = st
11317 .discovery_routes
11318 .iter()
11319 .filter_map(|(&side_id, route)| {
11320 let side = st.sides.get(side_id)?.as_ref()?;
11321 let announcers = route
11322 .announcers
11323 .iter()
11324 .map(|(sender_id, sender_state)| TopologyAnnouncerRoute {
11325 sender_id: sender_id.clone(),
11326 reachable_endpoints: sender_state
11327 .reachable
11328 .iter()
11329 .copied()
11330 .filter(|ep| !discovery::is_router_control_endpoint(*ep))
11331 .collect(),
11332 reachable_network_variables: sender_state
11333 .reachable_network_variables
11334 .clone(),
11335 reachable_timesync_sources: sender_state.reachable_timesync_sources.clone(),
11336 routers: sender_state.topology_boards.clone(),
11337 last_seen_ms: sender_state.last_seen_ms,
11338 age_ms: now_ms.saturating_sub(sender_state.last_seen_ms),
11339 })
11340 .collect();
11341 Some(TopologySideRoute {
11342 side_id,
11343 side_name: side.name.to_string(),
11344 reachable_endpoints: route
11345 .reachable
11346 .iter()
11347 .copied()
11348 .filter(|ep| !discovery::is_router_control_endpoint(*ep))
11349 .collect(),
11350 reachable_network_variables: route.reachable_network_variables.clone(),
11351 reachable_timesync_sources: route.reachable_timesync_sources.clone(),
11352 announcers,
11353 last_seen_ms: route.last_seen_ms,
11354 age_ms: now_ms.saturating_sub(route.last_seen_ms),
11355 })
11356 })
11357 .collect();
11358 let routers = self.advertised_discovery_topology_for_link_locked(&st, now_ms, true, None);
11359 let advertised_endpoints =
11360 self.advertised_discovery_endpoints_for_link_locked(&st, now_ms, true, None);
11361 let advertised_timesync_sources =
11362 self.advertised_discovery_timesync_sources_for_link_locked(&st, now_ms, None);
11363 let links = discovery::topology_links_from_boards(&routers);
11364 TopologySnapshot {
11365 advertised_endpoints,
11366 advertised_timesync_sources,
11367 routers,
11368 links,
11369 routes,
11370 current_announce_interval_ms: st.discovery_cadence.current_interval_ms,
11371 next_announce_ms: st.discovery_cadence.next_announce_ms,
11372 }
11373 }
11374
11375 #[cfg(feature = "discovery")]
11376 pub fn client_stats(&self, sender_id: &str) -> Option<ClientStatsSnapshot> {
11377 let now_ms = self.clock.now_ms();
11378 let st = self.state.lock();
11379 let mut side_ids = Vec::new();
11380 let mut side_names = Vec::new();
11381 let mut last_seen_ms = None::<u64>;
11382 let mut reachable_endpoints = Vec::new();
11383 let mut reachable_timesync_sources = Vec::new();
11384 let mut packets_sent = 0u64;
11385 let mut packets_received = 0u64;
11386 let mut bytes_sent = 0u64;
11387 let mut bytes_received = 0u64;
11388
11389 for (side_id, route) in &st.discovery_routes {
11390 let Some(sender_state) = route.announcers.get(sender_id) else {
11391 continue;
11392 };
11393 side_ids.push(*side_id);
11394 if let Some(side_name) = st
11395 .sides
11396 .get(*side_id)
11397 .and_then(|side| side.as_ref())
11398 .map(|side| side.name.clone())
11399 {
11400 side_names.push(side_name.to_string());
11401 }
11402 last_seen_ms = Some(last_seen_ms.unwrap_or(0).max(sender_state.last_seen_ms));
11403 reachable_endpoints.extend(sender_state.reachable.iter().copied());
11404 reachable_timesync_sources
11405 .extend(sender_state.reachable_timesync_sources.iter().cloned());
11406 if let Some(stats) = st.side_runtime_stats.get(side_id) {
11407 packets_sent = packets_sent.saturating_add(stats.tx_packets);
11408 packets_received = packets_received.saturating_add(stats.rx_packets);
11409 bytes_sent = bytes_sent.saturating_add(stats.tx_bytes);
11410 bytes_received = bytes_received.saturating_add(stats.rx_bytes);
11411 }
11412 }
11413
11414 if side_ids.is_empty() {
11415 return None;
11416 }
11417 reachable_endpoints.retain(|ep| !discovery::is_router_control_endpoint(*ep));
11418 crate::small_sort::sort(&mut reachable_endpoints);
11419 reachable_endpoints.dedup();
11420 reachable_timesync_sources.sort_unstable();
11421 reachable_timesync_sources.dedup();
11422 crate::small_sort::sort(&mut side_ids);
11423 side_ids.dedup();
11424 side_names.sort_unstable();
11425 side_names.dedup();
11426 let age_ms = last_seen_ms.map(|seen| now_ms.saturating_sub(seen));
11427 Some(ClientStatsSnapshot {
11428 sender_id: sender_id.to_string(),
11429 connected: age_ms.is_some_and(|age| age <= DISCOVERY_ROUTE_TTL_MS),
11430 side_ids,
11431 side_names,
11432 last_seen_ms,
11433 age_ms,
11434 reachable_endpoints,
11435 reachable_timesync_sources,
11436 packets_sent,
11437 packets_received,
11438 bytes_sent,
11439 bytes_received,
11440 })
11441 }
11442
11443 pub fn copy_reliable_pending_stats(
11447 &self,
11448 first: usize,
11449 rows: &mut [crate::diagnostics::ReliablePendingRow],
11450 ) -> usize {
11451 rows.fill(crate::diagnostics::ReliablePendingRow::default());
11452 let st = self.state.lock();
11453 for (row, (&id, sent)) in rows
11454 .iter_mut()
11455 .zip(st.end_to_end_reliable_tx.iter().skip(first))
11456 {
11457 row.packet_id = id;
11458 row.last_send_ms = sent.last_send_ms;
11459 row.data_type = Self::item_data_type(&sent.data).map_or(0, DataType::as_u32);
11460 row.pending_count = sent.pending_destinations.len().min(u32::MAX as usize) as u32;
11461 row.retries = sent.retries;
11462 row.flags = u32::from(sent.queued)
11463 | (u32::from(sent.waiting_for_predecessor) << 1)
11464 | (u32::from(
11465 st.reliable_return_routes
11466 .get(&id)
11467 .is_some_and(|route| route.upstream_contract.is_some()),
11468 ) << 2)
11469 | (u32::from(sent.tracked_destinations) << 3);
11470 for (out, owner) in row
11471 .pending_owners
11472 .iter_mut()
11473 .zip(sent.pending_destinations.keys())
11474 {
11475 *out = *owner;
11476 }
11477 }
11478 st.end_to_end_reliable_tx.len()
11479 }
11480
11481 pub fn export_reliable_pending_stats(&self) -> Vec<crate::diagnostics::ReliablePendingStats> {
11485 let st = self.state.lock();
11486 st.end_to_end_reliable_tx
11487 .iter()
11488 .filter_map(|(&packet_id, sent)| {
11489 Some(crate::diagnostics::ReliablePendingStats {
11490 packet_id,
11491 data_type: Self::item_data_type(&sent.data).ok()?,
11492 pending_destinations: sent.pending_destinations.keys().copied().collect(),
11493 retries: sent.retries,
11494 queued: sent.queued,
11495 waiting_for_predecessor: sent.waiting_for_predecessor,
11496 last_send_ms: sent.last_send_ms,
11497 })
11498 })
11499 .collect()
11500 }
11501
11502 pub fn export_runtime_stats(&self) -> RuntimeStatsSnapshot {
11503 let now_ms = self.clock.now_ms();
11504 let isr_rx = self.isr_rx_queue.snapshot().unwrap_or((0, 0));
11505 let st = self.state.lock();
11506
11507 let mut sides = Vec::new();
11508 for (side_id, side) in st.sides.iter().enumerate() {
11509 let Some(side) = side.as_ref() else { continue };
11510 let stats = st
11511 .side_runtime_stats
11512 .get(&side_id)
11513 .cloned()
11514 .unwrap_or_default();
11515 let adaptive = st
11516 .adaptive_route_stats
11517 .get(&side_id)
11518 .cloned()
11519 .unwrap_or_default()
11520 .snapshot(now_ms, true);
11521 let (tx_template_count, rx_template_count) = st
11522 .side_transport
11523 .get(&side_id)
11524 .map(|state| (state.tx_template_count(), state.rx_template_count()))
11525 .unwrap_or((0, 0));
11526 let mut data_types: Vec<RuntimeTypeStats> = stats
11527 .data_types
11528 .into_iter()
11529 .map(|(ty, item)| RuntimeTypeStats {
11530 data_type: DataType(ty),
11531 tx_packets: item.tx_packets,
11532 tx_bytes: item.tx_bytes,
11533 rx_packets: item.rx_packets,
11534 rx_bytes: item.rx_bytes,
11535 relayed_tx_packets: item.relayed_tx_packets,
11536 relayed_tx_bytes: item.relayed_tx_bytes,
11537 relayed_rx_packets: item.relayed_rx_packets,
11538 relayed_rx_bytes: item.relayed_rx_bytes,
11539 tx_retries: item.tx_retries,
11540 handler_failures: item.handler_failures,
11541 })
11542 .collect();
11543 data_types.sort_unstable_by_key(|item| item.data_type.as_u32());
11544 sides.push(RuntimeSideStats {
11545 side_id,
11546 side_name: side.name.to_string(),
11547 reliable_enabled: side.opts.reliable_enabled,
11548 link_local_enabled: side.opts.link_local_enabled,
11549 header_template_enabled: side.opts.header_template_enabled,
11550 max_frame_bytes: side.opts.max_frame_bytes,
11551 compact_header_target_bytes: side.opts.compact_header_target_bytes,
11552 side_transport_profile: side.opts.effective_transport_profile().as_str(),
11553 ingress_enabled: side.opts.ingress_enabled,
11554 egress_enabled: side.opts.egress_enabled,
11555 tx_packets: stats.tx_packets,
11556 tx_bytes: stats.tx_bytes,
11557 rx_packets: stats.rx_packets,
11558 rx_bytes: stats.rx_bytes,
11559 relayed_tx_packets: stats.relayed_tx_packets,
11560 relayed_tx_bytes: stats.relayed_tx_bytes,
11561 relayed_rx_packets: stats.relayed_rx_packets,
11562 relayed_rx_bytes: stats.relayed_rx_bytes,
11563 local_delivery_packets: stats.local_delivery_packets,
11564 tx_retries: stats.tx_retries,
11565 tx_handler_failures: stats.tx_handler_failures,
11566 local_handler_failures: stats.local_handler_failures,
11567 total_handler_retries: stats.total_handler_retries,
11568 side_transport_full_frames: stats.side_transport_full_frames,
11569 side_transport_compact_frames: stats.side_transport_compact_frames,
11570 side_transport_compact_delta_frames: stats.side_transport_compact_delta_frames,
11571 side_transport_compact_omitted_timestamp_frames: stats
11572 .side_transport_compact_omitted_timestamp_frames,
11573 side_transport_chunk_frames: stats.side_transport_chunk_frames,
11574 side_transport_raw_bytes: stats.side_transport_raw_bytes,
11575 side_transport_wire_bytes: stats.side_transport_wire_bytes,
11576 side_transport_bytes_saved: stats.side_transport_bytes_saved,
11577 side_transport_min_compact_overhead_bytes: stats
11578 .side_transport_min_compact_overhead_bytes,
11579 side_transport_max_compact_overhead_bytes: stats
11580 .side_transport_max_compact_overhead_bytes,
11581 side_transport_compact_target_misses: stats.side_transport_compact_target_misses,
11582 side_transport_template_evictions: stats.side_transport_template_evictions,
11583 side_transport_tx_template_count: tx_template_count,
11584 side_transport_rx_template_count: rx_template_count,
11585 max_side_transport_templates: side.opts.max_side_transport_templates,
11586 adaptive,
11587 data_types,
11588 });
11589 }
11590
11591 let mut route_modes: Vec<RouteModeStats> = st
11592 .route_selection_cursors
11593 .iter()
11594 .map(|(src, cursor)| RouteModeStats {
11595 src_side_id: *src,
11596 selection_mode: st.source_route_modes.get(src).copied(),
11597 cursor: *cursor,
11598 })
11599 .collect();
11600 for src in st.source_route_modes.keys() {
11601 if !route_modes.iter().any(|mode| mode.src_side_id == *src) {
11602 route_modes.push(RouteModeStats {
11603 src_side_id: *src,
11604 selection_mode: st.source_route_modes.get(src).copied(),
11605 cursor: 0,
11606 });
11607 }
11608 }
11609 route_modes.sort_unstable_by_key(|mode| mode.src_side_id.unwrap_or(usize::MAX));
11610
11611 let mut route_overrides: Vec<RouteOverrideStats> = st
11612 .route_overrides
11613 .iter()
11614 .map(|((src, dst), enabled)| RouteOverrideStats {
11615 src_side_id: *src,
11616 dst_side_id: *dst,
11617 enabled: *enabled,
11618 })
11619 .collect();
11620 route_overrides.sort_unstable_by_key(|item| {
11621 (item.src_side_id.unwrap_or(usize::MAX), item.dst_side_id)
11622 });
11623
11624 let mut typed_route_overrides: Vec<TypedRouteOverrideStats> = st
11625 .typed_route_overrides
11626 .iter()
11627 .map(|((src, ty, dst), enabled)| TypedRouteOverrideStats {
11628 src_side_id: *src,
11629 data_type: DataType(*ty),
11630 dst_side_id: *dst,
11631 enabled: *enabled,
11632 })
11633 .collect();
11634 typed_route_overrides.sort_unstable_by_key(|item| {
11635 (
11636 item.src_side_id.unwrap_or(usize::MAX),
11637 item.data_type.as_u32(),
11638 item.dst_side_id,
11639 )
11640 });
11641
11642 let mut route_weights: Vec<RouteWeightStats> = st
11643 .route_weights
11644 .iter()
11645 .map(|((src, dst), weight)| RouteWeightStats {
11646 src_side_id: *src,
11647 dst_side_id: *dst,
11648 weight: *weight,
11649 })
11650 .collect();
11651 route_weights.sort_unstable_by_key(|item| {
11652 (item.src_side_id.unwrap_or(usize::MAX), item.dst_side_id)
11653 });
11654
11655 let mut route_priorities: Vec<RoutePriorityStats> = st
11656 .route_priorities
11657 .iter()
11658 .map(|((src, dst), priority)| RoutePriorityStats {
11659 src_side_id: *src,
11660 dst_side_id: *dst,
11661 priority: *priority,
11662 })
11663 .collect();
11664 route_priorities.sort_unstable_by_key(|item| {
11665 (item.src_side_id.unwrap_or(usize::MAX), item.dst_side_id)
11666 });
11667
11668 #[cfg(feature = "discovery")]
11669 let discovery = DiscoveryRuntimeStats {
11670 route_count: st.discovery_routes.len(),
11671 announcer_count: st
11672 .discovery_routes
11673 .values()
11674 .map(|route| route.announcers.len())
11675 .sum(),
11676 current_announce_interval_ms: Some(st.discovery_cadence.current_interval_ms),
11677 next_announce_ms: Some(st.discovery_cadence.next_announce_ms),
11678 };
11679 #[cfg(not(feature = "discovery"))]
11680 let discovery = DiscoveryRuntimeStats {
11681 route_count: 0,
11682 announcer_count: 0,
11683 current_announce_interval_ms: None,
11684 next_announce_ms: None,
11685 };
11686
11687 RuntimeStatsSnapshot {
11688 sides,
11689 route_modes,
11690 route_overrides,
11691 typed_route_overrides,
11692 route_weights,
11693 route_priorities,
11694 queues: QueueRuntimeStats {
11695 rx_len: isr_rx.0.saturating_add(st.received_queue.len()),
11696 rx_bytes: isr_rx.1.saturating_add(st.received_queue.bytes_used()),
11697 tx_len: st.transmit_queue.len(),
11698 tx_bytes: st.transmit_queue.bytes_used(),
11699 replay_len: 0,
11700 replay_bytes: 0,
11701 recent_rx_len: st.recent_rx.len(),
11702 recent_rx_bytes: st.recent_rx.bytes_used(),
11703 reliable_rx_buffered_len: st.reliable_rx_buffer_len(),
11704 reliable_rx_buffered_bytes: st.reliable_rx_buffered_bytes(),
11705 shared_queue_bytes_used: st.shared_queue_bytes_used(),
11706 },
11707 reliable: ReliableRuntimeStats {
11708 reliable_return_route_count: st.reliable_return_routes.len(),
11709 end_to_end_pending_count: st.end_to_end_reliable_tx.len(),
11710 end_to_end_pending_destination_count: st
11711 .end_to_end_reliable_tx
11712 .values()
11713 .map(|sent| sent.pending_destinations.len())
11714 .sum(),
11715 end_to_end_acked_cache_count: 0,
11716 },
11717 discovery,
11718 total_handler_failures: st.total_handler_failures,
11719 total_handler_retries: st.total_handler_retries,
11720 }
11721 }
11722
11723 pub fn export_memory_layout_json(&self) -> String {
11725 let isr_rx = self.isr_rx_queue.snapshot().unwrap_or((0, 0));
11726 let st = self.state.lock();
11727 #[cfg(feature = "discovery")]
11728 let discovery_bytes = st.discovery_bytes_used();
11729 #[cfg(not(feature = "discovery"))]
11730 let discovery_bytes = 0usize;
11731 let schema_bytes = crate::config::schema_bytes_used();
11732 let network_variable_cache_bytes = st
11733 .managed_variable_latest
11734 .values()
11735 .map(|entry| entry.packet.byte_cost())
11736 .sum::<usize>();
11737 let mut out = String::new();
11738 let memory = st.memory;
11739 let _ = fmt::Write::write_fmt(
11740 &mut out,
11741 format_args!(
11742 "{{\"kind\":\"router\",\
11743 \"shared_queue_bytes_used\":{},\"shared_queue_bytes_allocated\":{},\
11744 \"rx_queue_bytes_used\":{},\"rx_queue_bytes_allocated\":{},\"rx_queue_len\":{},\
11745 \"isr_rx_queue_bytes_used\":{},\"isr_rx_queue_bytes_allocated\":{},\"isr_rx_queue_len\":{},\
11746 \"tx_queue_bytes_used\":{},\"tx_queue_bytes_allocated\":{},\"tx_queue_len\":{},\
11747 \"recent_rx_bytes_used\":{},\"recent_rx_bytes_allocated\":{},\"recent_rx_len\":{},\
11748 \"reliable_rx_buffer_bytes_used\":{},\"reliable_rx_buffer_bytes_allocated\":{},\"reliable_rx_buffer_len\":{},\
11749 \"discovery_bytes_used\":{},\"discovery_bytes_allocated\":{},\
11750 \"schema_bytes_used\":{},\"schema_bytes_allocated\":{},\
11751 \"network_variable_cache_bytes_used\":{},\"network_variable_cache_bytes_allocated\":{},\"network_variable_cache_len\":{}}}",
11752 st.shared_queue_bytes_used(),
11753 memory.max_queue_budget,
11754 st.received_queue.bytes_used(),
11755 st.received_queue.max_bytes(),
11756 st.received_queue.len(),
11757 isr_rx.1,
11758 memory.max_queue_budget,
11759 isr_rx.0,
11760 st.transmit_queue.bytes_used(),
11761 st.transmit_queue.max_bytes(),
11762 st.transmit_queue.len(),
11763 st.recent_rx.bytes_used(),
11764 st.recent_rx.max_bytes(),
11765 st.recent_rx.len(),
11766 st.reliable_rx_buffered_bytes(),
11767 memory.max_queue_budget,
11768 st.reliable_rx_buffer_len(),
11769 discovery_bytes,
11770 memory.max_queue_budget,
11771 schema_bytes,
11772 memory.max_queue_budget,
11773 network_variable_cache_bytes,
11774 memory.max_queue_budget,
11775 st.managed_variable_latest.len(),
11776 ),
11777 );
11778 out
11779 }
11780
11781 #[cfg(test)]
11782 pub(crate) fn debug_end_to_end_pending_destination_count(
11783 &self,
11784 packet_id: u64,
11785 ) -> Option<usize> {
11786 let st = self.state.lock();
11787 st.end_to_end_reliable_tx
11788 .get(&packet_id)
11789 .map(|sent| sent.pending_destinations.len())
11790 }
11791
11792 #[cfg(test)]
11793 pub(crate) fn debug_end_to_end_tracked_count(&self) -> usize {
11794 let st = self.state.lock();
11795 st.end_to_end_reliable_tx.len()
11796 }
11797
11798 #[cfg(test)]
11799 pub(crate) fn debug_reliable_return_route_count(&self) -> usize {
11800 let st = self.state.lock();
11801 st.reliable_return_routes.len()
11802 }
11803
11804 #[cfg(test)]
11805 pub(crate) fn debug_side_storage(&self) -> (usize, usize) {
11806 let st = self.state.lock();
11807 (st.sides.len(), st.sides.capacity())
11808 }
11809
11810 #[cfg(test)]
11811 pub(crate) fn debug_queue_lengths(&self) -> (usize, usize, usize) {
11812 let st = self.state.lock();
11813 (
11814 st.received_queue.len(),
11815 st.transmit_queue.len(),
11816 st.recent_rx.len(),
11817 )
11818 }
11819
11820 #[cfg(test)]
11821 pub(crate) fn debug_shared_queue_bytes_used(&self) -> usize {
11822 let st = self.state.lock();
11823 st.shared_queue_bytes_used()
11824 }
11825
11826 #[cfg(test)]
11827 pub(crate) fn debug_recent_rx_capacity(&self) -> (usize, usize) {
11828 let st = self.state.lock();
11829 (st.recent_rx.capacity(), st.recent_rx.max_bytes())
11830 }
11831
11832 fn get_hash(item: &RouterItem) -> u64 {
11836 match item {
11837 RouterItem::Packet(pkt) => pkt.packet_id(),
11838 RouterItem::Packed(bytes) => {
11839 match wire_format::packet_id_from_wire(bytes.as_ref()) {
11840 Ok(id) => id,
11841 Err(_e) => {
11842 let h: u64 = 0x9E37_79B9_7F4A_7C15;
11845 hash_bytes_u64(h, bytes.as_ref())
11846 }
11847 }
11848 }
11849 }
11850 }
11851
11852 fn remove_pkt_id(&self, item: &RouterItem) {
11854 let hash = Self::get_hash(item);
11855 let mut st = self.state.lock();
11856 st.recent_rx.remove_value(&hash);
11857 }
11858
11859 fn is_duplicate_pkt(&self, item: &RouterItem) -> TelemetryResult<bool> {
11862 let id = Self::get_hash(item);
11863 let mut st = self.state.lock();
11864 if st.recent_rx.contains(&id) {
11865 Ok(true)
11866 } else {
11867 st.push_recent_rx(id)?;
11868 Ok(false)
11869 }
11870 }
11871
11872 fn handle_callback_error(
11877 &self,
11878 pkt: &Packet,
11879 dest: Option<DataEndpoint>,
11880 e: TelemetryError,
11881 called_from_queue: bool,
11882 ) -> TelemetryResult<()> {
11883 let device = self.sender_arc();
11884 let error_msg = match dest {
11885 Some(failed_local) => format!(
11886 "Handler for endpoint {:?} failed on device {:?}: {:?}",
11887 failed_local, device, e
11888 ),
11889 None => format!("TX Handler failed on device {:?}: {:?}", device, e),
11890 };
11891
11892 let mut recipients: Vec<DataEndpoint> = pkt
11893 .endpoints()
11894 .iter()
11895 .copied()
11896 .filter(|&ep| self.cfg.is_local_endpoint(ep))
11897 .collect();
11898 crate::small_sort::sort(&mut recipients);
11899 recipients.dedup();
11900
11901 if let Some(failed_local) = dest {
11902 recipients.retain(|&ep| ep != failed_local);
11903 }
11904
11905 if recipients.is_empty() && dest.is_none() {
11909 fallback_stdout(&error_msg);
11910 return Ok(());
11911 }
11912
11913 if recipients.is_empty() {
11916 recipients = pkt.endpoints().to_vec();
11917 crate::small_sort::sort(&mut recipients);
11918 recipients.dedup();
11919 if let Some(failed_local) = dest {
11920 recipients.retain(|&ep| ep != failed_local);
11921 }
11922 }
11923
11924 if recipients.is_empty() {
11925 fallback_stdout(&error_msg);
11926 return Ok(());
11927 }
11928
11929 let payload = make_error_payload(&error_msg);
11930
11931 let sender = self.sender_arc();
11932 let error_pkt = Packet::new(
11933 DataType::TelemetryError,
11934 &recipients,
11935 sender.as_ref(),
11936 self.packet_timestamp_ms(),
11937 crate::shared_bytes::convert(payload),
11938 )?;
11939
11940 self.emit_internal_tx(
11941 RouterTxItem::Broadcast(RouterItem::Packet(error_pkt)),
11942 false,
11943 called_from_queue,
11944 )
11945 }
11946
11947 #[inline]
11951 pub fn process_tx_queue(&self) -> TelemetryResult<()> {
11952 self.process_tx_queue_with_timeout(0)
11953 }
11954
11955 #[inline]
11957 pub fn process_all_queues(&self) -> TelemetryResult<()> {
11958 self.process_all_queues_with_timeout(0)
11959 }
11960
11961 #[inline]
11963 pub fn clear_queues(&self) {
11964 let mut st = self.state.lock();
11965 st.transmit_queue.clear();
11966 st.received_queue.clear();
11967 drop(st);
11968 let _ = self.isr_rx_queue.clear();
11969 }
11970
11971 #[inline]
11973 pub fn clear_rx_queue(&self) {
11974 let mut st = self.state.lock();
11975 st.received_queue.clear();
11976 drop(st);
11977 let _ = self.isr_rx_queue.clear();
11978 }
11979
11980 #[inline]
11982 pub fn clear_tx_queue(&self) {
11983 let mut st = self.state.lock();
11984 st.transmit_queue.clear();
11985 st.tx_priority_burst = 0;
11986 }
11987
11988 fn pop_transmit_queue_locked(st: &mut RouterInner, now_ms: u64) -> Option<TxQueued> {
11989 st.transmit_queue.pop_front_fair_if(
11990 &mut st.tx_priority_burst,
11991 MAX_PRIORITY_BURST,
11992 |item| item.priority,
11993 |item| item.retry_after_ms <= now_ms,
11994 )
11995 }
11996
11997 fn process_tx_queue_with_timeout_impl(&self, timeout_ms: u32) -> TelemetryResult<()> {
12000 self.expire_side_chunks();
12001 let start = self.clock.now_ms();
12002 loop {
12003 let received_control = crate::memory_admission::allow_drain(
12004 self.drain_queued_control_rx_before_tx(start, timeout_ms),
12005 )?;
12006 let control_budget_used = received_control
12007 && timeout_ms != 0
12008 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms);
12009 if !control_budget_used {
12012 crate::memory_admission::allow_drain(self.process_reliable_timeouts())?;
12013 crate::memory_admission::allow_drain(self.process_end_to_end_reliable_timeouts())?;
12014 }
12015
12016 let pkt_opt = {
12017 let mut st = self.state.lock();
12018 Self::pop_transmit_queue_locked(&mut st, self.clock.now_ms())
12019 };
12020 let Some(pkt) = pkt_opt else { break };
12021 self.tx_item_impl(pkt.item, pkt.ignore_local, true)?;
12022 if timeout_ms != 0 && self.clock.now_ms().wrapping_sub(start) >= timeout_ms as u64 {
12023 break;
12024 }
12025 }
12026 Ok(())
12027 }
12028
12029 pub fn process_tx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
12032 #[cfg(feature = "timesync")]
12033 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
12034 #[cfg(feature = "discovery")]
12035 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
12036 self.process_tx_queue_with_timeout_impl(timeout_ms)
12037 }
12038
12039 pub fn dispatch_tx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
12045 self.process_tx_queue_with_timeout_impl(timeout_ms)
12046 }
12047
12048 #[inline]
12050 fn process_rx_queue_item(&self, item: RouterRxItem) -> TelemetryResult<()> {
12051 self.rx_item(&item, true)
12052 }
12053
12054 fn process_rx_queue_with_timeout_impl(&self, timeout_ms: u32) -> TelemetryResult<()> {
12057 self.expire_side_chunks();
12058 let start = self.clock.now_ms();
12059 loop {
12060 let item_opt = self.isr_rx_queue.pop_front().unwrap_or(None).or_else(|| {
12061 let mut st = self.state.lock();
12062 st.received_queue.pop_front()
12063 });
12064 let Some(item) = item_opt else { break };
12065 self.process_rx_queue_item(item)?;
12066 if timeout_ms != 0 && self.clock.now_ms().wrapping_sub(start) >= timeout_ms as u64 {
12067 break;
12068 }
12069 }
12070 Ok(())
12071 }
12072
12073 pub fn process_rx_queue_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
12076 #[cfg(feature = "timesync")]
12077 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
12078 #[cfg(feature = "discovery")]
12079 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
12080 self.process_rx_queue_with_timeout_impl(timeout_ms)
12081 }
12082
12083 fn process_all_queues_with_timeout_impl(&self, timeout_ms: u32) -> TelemetryResult<()> {
12086 self.expire_side_chunks();
12087 let start = self.clock.now_ms();
12092 loop {
12093 let received_control = crate::memory_admission::allow_drain(
12094 self.drain_queued_control_rx_before_tx(start, timeout_ms),
12095 )?;
12096 let mut did_any = received_control;
12097 let control_budget_used = received_control
12098 && timeout_ms != 0
12099 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms);
12100 if !control_budget_used {
12101 crate::memory_admission::allow_drain(self.process_reliable_timeouts())?;
12102 crate::memory_admission::allow_drain(self.process_end_to_end_reliable_timeouts())?;
12103 }
12104
12105 if let Some(pkt) = {
12106 let mut st = self.state.lock();
12107 let now = self.clock.now_ms();
12108 let inner = &mut *st;
12109 inner.transmit_queue.pop_front_fair_if(
12110 &mut inner.tx_priority_burst,
12111 MAX_PRIORITY_BURST,
12112 |item| item.priority,
12113 |item| {
12114 item.retry_after_ms <= now && (!control_budget_used || item.priority >= 200)
12115 },
12116 )
12117 } {
12118 self.tx_item_impl(pkt.item, pkt.ignore_local, true)?;
12119 did_any = true;
12120 }
12121 if control_budget_used {
12123 return Ok(());
12124 }
12125 if let Some(item) = self.isr_rx_queue.pop_front().unwrap_or(None).or_else(|| {
12126 let mut st = self.state.lock();
12127 st.received_queue.pop_front()
12128 }) {
12129 self.process_rx_queue_item(item)?;
12130 did_any = true;
12131 }
12132 if !did_any
12133 || (timeout_ms != 0
12134 && self.clock.now_ms().wrapping_sub(start) >= u64::from(timeout_ms))
12135 {
12136 break;
12137 }
12138 }
12139
12140 Ok(())
12141 }
12142
12143 pub fn process_all_queues_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
12146 #[cfg(feature = "timesync")]
12147 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
12148 #[cfg(feature = "discovery")]
12149 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
12150 self.process_all_queues_with_timeout_impl(timeout_ms)
12151 }
12152
12153 pub fn drain_queues_with_timeout(&self, timeout_ms: u32) -> TelemetryResult<()> {
12158 self.process_all_queues_with_timeout_impl(timeout_ms)
12159 }
12160
12161 pub fn periodic(&self, timeout_ms: u32) -> TelemetryResult<()> {
12166 #[cfg(feature = "timesync")]
12167 let _ = crate::memory_admission::allow_drain(self.poll_timesync())?;
12168
12169 #[cfg(feature = "discovery")]
12170 {
12171 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
12172 }
12173
12174 self.process_all_queues_with_timeout_impl(timeout_ms)
12175 }
12176
12177 pub fn periodic_no_timesync(&self, timeout_ms: u32) -> TelemetryResult<()> {
12182 #[cfg(feature = "discovery")]
12183 {
12184 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
12185 }
12186
12187 self.process_all_queues_with_timeout_impl(timeout_ms)
12188 }
12189
12190 #[inline]
12192 fn tx_queue_item_with_flags(
12193 &self,
12194 item: RouterTxItem,
12195 ignore_local: bool,
12196 ) -> TelemetryResult<()> {
12197 self.tx_queue_item_with_flags_at(item, ignore_local, 0)
12198 }
12199
12200 fn tx_queue_item_for_retry(
12201 &self,
12202 item: RouterTxItem,
12203 ignore_local: bool,
12204 ) -> TelemetryResult<()> {
12205 self.tx_queue_item_with_flags_at(item, ignore_local, self.clock.now_ms().saturating_add(1))
12206 }
12207
12208 fn tx_queue_item_with_flags_at(
12209 &self,
12210 item: RouterTxItem,
12211 ignore_local: bool,
12212 retry_after_ms: u64,
12213 ) -> TelemetryResult<()> {
12214 let priority = match &item {
12215 RouterTxItem::Broadcast(data) => self.item_service_priority(data)?,
12216 RouterTxItem::EndToEndReplay { .. } => {
12217 Self::router_item_priority_bumped(DataType::ReliableAck)
12218 }
12219 RouterTxItem::ToSide { data, .. } => self.item_service_priority(data)?,
12220 RouterTxItem::ReliableReplay { bytes, .. } => {
12221 let ty = wire_format::peek_routing_envelope(bytes.as_ref())?.ty;
12222 Self::router_item_priority_bumped(ty)
12223 }
12224 };
12225 self.tx_queue_item_with_priority_at(item, ignore_local, priority, retry_after_ms)
12226 }
12227
12228 #[inline]
12229 fn tx_queue_item_with_priority(
12230 &self,
12231 item: RouterTxItem,
12232 ignore_local: bool,
12233 priority: u8,
12234 ) -> TelemetryResult<()> {
12235 self.tx_queue_item_with_priority_at(item, ignore_local, priority, 0)
12236 }
12237
12238 fn tx_queue_item_with_priority_at(
12239 &self,
12240 item: RouterTxItem,
12241 ignore_local: bool,
12242 priority: u8,
12243 retry_after_ms: u64,
12244 ) -> TelemetryResult<()> {
12245 let mut st = self.state.lock();
12246 st.push_transmit(TxQueued {
12247 item,
12248 ignore_local,
12249 priority,
12250 retry_after_ms,
12251 })?;
12252 Ok(())
12253 }
12254
12255 #[inline]
12257 fn tx_queue_item(&self, item: RouterTxItem) -> TelemetryResult<()> {
12258 self.tx_queue_item_with_flags(item, false)
12259 }
12260
12261 #[inline]
12262 fn try_enter_side_tx(&self) -> Option<crate::lock::ReentryGuard<'_>> {
12263 self.side_tx_gate.try_enter()
12264 }
12265
12266 #[inline]
12267 fn side_tx_active(&self) -> bool {
12268 self.side_tx_gate.is_active()
12269 }
12270
12271 #[inline]
12275 pub fn process_rx_queue(&self) -> TelemetryResult<()> {
12276 self.process_rx_queue_with_timeout(0)
12277 }
12278
12279 #[inline]
12281 pub fn rx_packed_queue(&self, bytes: &[u8]) -> TelemetryResult<()> {
12282 crate::memory_admission::check_receive(bytes)?;
12283 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
12284 let priority = self.item_service_priority(&data)?;
12285 let mut st = self.state.lock();
12286 st.push_received(RouterRxItem {
12287 src: None,
12288 data,
12289 priority,
12290 })?;
12291 Ok(())
12292 }
12293
12294 #[inline]
12299 pub fn rx_packed_queue_isr(&self, bytes: &[u8]) -> TelemetryResult<()> {
12300 crate::memory_admission::check_receive(bytes)?;
12301 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
12302 let priority = Self::router_item_priority(&data)?;
12303 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
12304 src: None,
12305 data,
12306 priority,
12307 })
12308 }
12309
12310 #[inline]
12312 pub fn rx_queue(&self, pkt: Packet) -> TelemetryResult<()> {
12313 pkt.validate()?;
12314 let data = RouterItem::Packet(pkt);
12315 let priority = self.item_service_priority(&data)?;
12316 let mut st = self.state.lock();
12317 st.push_received(RouterRxItem {
12318 src: None,
12319 data,
12320 priority,
12321 })?;
12322 Ok(())
12323 }
12324
12325 #[inline]
12330 pub fn rx_queue_isr(&self, pkt: Packet) -> TelemetryResult<()> {
12331 pkt.validate()?;
12332 let data = RouterItem::Packet(pkt);
12333 let priority = Self::router_item_priority(&data)?;
12334 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
12335 src: None,
12336 data,
12337 priority,
12338 })
12339 }
12340
12341 #[inline]
12343 pub fn rx_queue_from_side(&self, pkt: Packet, side: RouterSideId) -> TelemetryResult<()> {
12344 self.ensure_side_ingress_enabled(side)?;
12345 pkt.validate()?;
12346 let data = RouterItem::Packet(pkt);
12347 let priority = self.item_service_priority(&data)?;
12348 let mut st = self.state.lock();
12349 st.push_received(RouterRxItem {
12350 src: Some(side),
12351 data,
12352 priority,
12353 })?;
12354 Ok(())
12355 }
12356
12357 #[inline]
12362 pub fn rx_queue_from_side_isr(&self, pkt: Packet, side: RouterSideId) -> TelemetryResult<()> {
12363 self.ensure_side_ingress_enabled(side)?;
12364 pkt.validate()?;
12365 let data = RouterItem::Packet(pkt);
12366 let priority = Self::router_item_priority(&data)?;
12367 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
12368 src: Some(side),
12369 data,
12370 priority,
12371 })
12372 }
12373
12374 #[inline]
12376 pub fn rx_packed_queue_from_side(
12377 &self,
12378 bytes: &[u8],
12379 side: RouterSideId,
12380 ) -> TelemetryResult<()> {
12381 self.ensure_side_ingress_enabled(side)?;
12382 let Some(decoded) = self.decode_side_transport_frame(side, bytes)? else {
12383 return Ok(());
12384 };
12385 let data = RouterItem::Packed(decoded);
12386 let priority = self.item_service_priority(&data)?;
12387 let mut st = self.state.lock();
12388 st.push_received(RouterRxItem {
12389 src: Some(side),
12390 data,
12391 priority,
12392 })?;
12393 Ok(())
12394 }
12395
12396 #[inline]
12401 pub fn rx_packed_queue_from_side_isr(
12402 &self,
12403 bytes: &[u8],
12404 side: RouterSideId,
12405 ) -> TelemetryResult<()> {
12406 self.ensure_side_ingress_enabled(side)?;
12407 crate::memory_admission::check_receive(bytes)?;
12408 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
12409 let priority = Self::router_item_priority(&data)?;
12410 self.isr_rx_queue.push_back_prioritized(RouterRxItem {
12411 src: Some(side),
12412 data,
12413 priority,
12414 })
12415 }
12416
12417 fn retry_with_attempts<F, T>(
12418 &self,
12419 times: usize,
12420 f: F,
12421 ) -> Result<(T, usize), (TelemetryError, usize)>
12422 where
12423 F: Fn() -> TelemetryResult<T>,
12424 {
12425 let mut last_err = None;
12426 for attempt in 0..times {
12427 match f() {
12428 Ok(v) => return Ok((v, attempt + 1)),
12429 Err(e) if Self::is_side_tx_busy(&e) => return Err((e, attempt + 1)),
12432 Err(e) => last_err = Some((e, attempt + 1)),
12433 }
12434 }
12435 Err(last_err.expect("times > 0"))
12436 }
12437
12438 #[inline]
12440 fn endpoint_has_packet_handler(&self, ep: DataEndpoint) -> bool {
12441 self.cfg
12442 .handlers
12443 .iter()
12444 .any(|h| h.endpoint == ep && matches!(h.handler, EndpointHandlerFn::Packet(_)))
12445 }
12446
12447 #[inline]
12449 fn endpoint_has_packed_handler(&self, ep: DataEndpoint) -> bool {
12450 self.cfg
12451 .handlers
12452 .iter()
12453 .any(|h| h.endpoint == ep && matches!(h.handler, EndpointHandlerFn::Packed(_)))
12454 }
12455
12456 fn packet_has_local_handler(&self, pkt: &Packet) -> bool {
12457 pkt.endpoints()
12458 .iter()
12459 .copied()
12460 .any(|ep| self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep))
12461 }
12462
12463 fn packet_has_local_delivery(&self, pkt: &Packet) -> bool {
12470 self.packet_has_local_handler(pkt) || self.is_managed_variable_type(pkt.data_type())
12471 }
12472
12473 fn call_handler_with_retries(
12479 &self,
12480 dest: DataEndpoint,
12481 handler: &EndpointHandler,
12482 data: Option<&[u8]>,
12483 pkt_for_ctx: Option<&Packet>,
12484 env_for_ctx: Option<&wire_format::TelemetryEnvelope>,
12485 called_from_queue: bool,
12486 ) -> TelemetryResult<()> {
12487 let owned_tmp: Option<RouterItem>;
12488
12489 let item_for_ctx: &RouterItem = match (data, pkt_for_ctx) {
12490 (Some(d), _) => {
12491 owned_tmp = Some(RouterItem::Packed(crate::shared_bytes::convert(
12492 Arc::<[u8]>::from(d),
12493 )));
12494 owned_tmp.as_ref().unwrap()
12495 }
12496 (None, Some(pkt)) => {
12497 owned_tmp = Some(RouterItem::Packet(pkt.clone()));
12498 owned_tmp.as_ref().unwrap()
12499 }
12500 (None, None) => {
12501 debug_assert!(
12502 false,
12503 "call_handler_with_retries called without data or packet context"
12504 );
12505 return Ok(());
12506 }
12507 };
12508
12509 match (&handler.handler, data) {
12510 (EndpointHandlerFn::Packet(f), _) => {
12511 let pkt = pkt_for_ctx.expect("Packet handler requires Packet context");
12512 with_retries(
12513 self,
12514 dest,
12515 item_for_ctx,
12516 pkt_for_ctx,
12517 env_for_ctx,
12518 called_from_queue,
12519 || f(pkt),
12520 )
12521 }
12522
12523 (EndpointHandlerFn::Packed(f), Some(bytes)) => with_retries(
12524 self,
12525 dest,
12526 item_for_ctx,
12527 pkt_for_ctx,
12528 env_for_ctx,
12529 called_from_queue,
12530 || f(bytes),
12531 ),
12532
12533 (EndpointHandlerFn::Packed(_), None) => Ok(()),
12534 }
12535 }
12536
12537 fn handle_callback_error_from_env(
12542 &self,
12543 env: &wire_format::TelemetryEnvelope,
12544 dest: Option<DataEndpoint>,
12545 e: TelemetryError,
12546 called_from_queue: bool,
12547 ) -> TelemetryResult<()> {
12548 let mut recipients: Vec<DataEndpoint> = env
12549 .endpoints
12550 .iter()
12551 .copied()
12552 .filter(|&ep| self.cfg.is_local_endpoint(ep))
12553 .collect();
12554 crate::small_sort::sort(&mut recipients);
12555 recipients.dedup();
12556 if let Some(failed) = dest {
12557 recipients.retain(|&ep| ep != failed);
12558 }
12559
12560 let device = self.sender_arc();
12561 let error_msg = format!(
12562 "Handler for endpoint {:?} failed on device {:?}: {:?}",
12563 dest, device, e
12564 );
12565
12566 if recipients.is_empty() && dest.is_none() {
12567 fallback_stdout(&error_msg);
12568 return Ok(());
12569 }
12570
12571 if recipients.is_empty() {
12572 recipients = env.endpoints.to_vec();
12573 crate::small_sort::sort(&mut recipients);
12574 recipients.dedup();
12575 if let Some(failed) = dest {
12576 recipients.retain(|&ep| ep != failed);
12577 }
12578 }
12579
12580 if recipients.is_empty() {
12581 fallback_stdout(&error_msg);
12582 return Ok(());
12583 }
12584
12585 let payload = make_error_payload(&error_msg);
12586
12587 let error_pkt = Packet::new(
12588 DataType::TelemetryError,
12589 &recipients,
12590 &env.sender.clone(),
12591 env.timestamp_ms,
12592 crate::shared_bytes::convert(payload),
12593 )?;
12594 self.emit_internal_tx(
12595 RouterTxItem::Broadcast(RouterItem::Packet(error_pkt)),
12596 false,
12597 called_from_queue,
12598 )
12599 }
12600
12601 fn handle_internal_reliable_packet(
12602 &self,
12603 pkt: &Packet,
12604 src: Option<RouterSideId>,
12605 called_from_queue: bool,
12606 ) -> TelemetryResult<bool> {
12607 if !matches!(
12608 pkt.data_type(),
12609 DataType::ReliableAck | DataType::ReliablePartialAck | DataType::ReliablePacketRequest
12610 ) {
12611 return Ok(false);
12612 }
12613
12614 let Some(src) = src else {
12615 return Ok(false);
12616 };
12617
12618 if Self::is_end_to_end_ack_packet(pkt)
12619 && let Ok(packet_id) = Self::decode_end_to_end_reliable_ack(pkt.payload())
12620 {
12621 let alias = {
12622 let st = self.state.lock();
12623 st.reliable_return_routes
12624 .get(&packet_id)
12625 .filter(|route| route.upstream_contract.is_some())
12626 .cloned()
12627 };
12628 if let Some(alias) = alias {
12629 let (upstream_id, publisher_hash) = alias.upstream_contract.expect("checked alias");
12630 #[cfg(feature = "std")]
12631 if reliable_trace_enabled() {
12632 eprintln!(
12633 "[sedsnet-reliable] proxy receipt id={packet_id:#x} upstream={upstream_id:#x} owner={:?}",
12634 Self::end_to_end_ack_sender_hash(pkt)
12635 );
12636 }
12637 let completed = {
12638 let mut st = self.state.lock();
12639 if let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) {
12640 if let Some(owner) = Self::end_to_end_ack_sender_hash(pkt) {
12641 sent.pending_destinations.remove(&owner);
12642 }
12643 if !sent.pending_destinations.is_empty() {
12644 return Ok(true);
12645 }
12646 st.end_to_end_reliable_tx.remove(&packet_id);
12647 true
12648 } else {
12649 return Ok(true);
12652 }
12653 };
12654 if completed {
12655 let mut st = self.state.lock();
12656 if Self::remember_reliable_return_route_locked(&mut st, upstream_id) {
12657 st.reliable_return_routes.insert(
12658 upstream_id,
12659 ReliableReturnRouteState {
12660 side: alias.side,
12661 last_replay_ms: alias.last_replay_ms,
12662 upstream_contract: None,
12663 },
12664 );
12665 }
12666 }
12667 self.queue_end_to_end_reliable_ack_for_sender_hash(
12668 upstream_id,
12669 publisher_hash,
12670 called_from_queue,
12671 )?;
12672 self.release_ordered_successors()?;
12673 return Ok(true);
12674 }
12675 let targets_local_sender = pkt.wire_target_senders().is_empty()
12682 || pkt
12683 .wire_target_senders()
12684 .contains(&Self::sender_hash(self.sender_arc().as_ref()));
12685 let mut st = self.state.lock();
12686 if let Some(sent) = st.end_to_end_reliable_tx.get_mut(&packet_id) {
12687 if sent.waiting_for_predecessor {
12688 return Ok(targets_local_sender);
12689 }
12690 if let Some(sender_hash) = Self::end_to_end_ack_sender_hash(pkt) {
12691 let _removed = sent.pending_destinations.remove(&sender_hash);
12692 #[cfg(feature = "std")]
12693 if reliable_trace_enabled() {
12694 eprintln!(
12695 "[sedsnet-reliable] receipt time={} id={packet_id:#x} sender={sender_hash:#x} matched={} remaining={:?}",
12696 self.clock.now_ms(),
12697 _removed.is_some(),
12698 sent.pending_destinations
12699 );
12700 }
12701 if sent.pending_destinations.is_empty() {
12702 st.end_to_end_reliable_tx.remove(&packet_id);
12703 }
12704 drop(st);
12705 self.release_ordered_successors()?;
12706 return Ok(targets_local_sender);
12707 }
12708 st.end_to_end_reliable_tx.remove(&packet_id);
12709 drop(st);
12710 self.release_ordered_successors()?;
12711 return Ok(targets_local_sender);
12712 }
12713 return Ok(targets_local_sender);
12716 }
12717
12718 let vals = pkt.data_as_u32()?;
12719 if vals.len() != 2 {
12720 return Err(TelemetryError::Unpack("bad reliable control payload"));
12721 }
12722 let ty = DataType::try_from_u32(vals[0]).ok_or(TelemetryError::InvalidType)?;
12723 let seq = vals[1];
12724
12725 match pkt.data_type() {
12726 DataType::ReliableAck => {
12727 self.handle_reliable_ack(src, ty, seq);
12728 Ok(true)
12729 }
12730 DataType::ReliablePartialAck => {
12731 self.handle_reliable_partial_ack(src, ty, seq);
12732 Ok(true)
12733 }
12734 DataType::ReliablePacketRequest => {
12735 self.queue_reliable_retransmit(src, ty, seq, called_from_queue)?;
12736 Ok(true)
12737 }
12738 _ => Ok(false),
12739 }
12740 }
12741
12742 fn rx_item(&self, item: &RouterRxItem, called_from_queue: bool) -> TelemetryResult<()> {
12748 if crate::memory_admission::supports_work_kinds()
12749 && let (Some(side), RouterItem::Packed(bytes)) = (item.src, &item.data)
12750 {
12751 self.admit_received_frame(side, bytes)?;
12752 }
12753 if let Some(src) = item.src {
12754 self.ensure_side_ingress_enabled(src)?;
12755 match &item.data {
12756 RouterItem::Packet(pkt) => {
12757 let bytes = wire_format::packet_wire_size(pkt);
12758 self.note_side_rx(src, pkt.data_type(), bytes, true);
12759 }
12760 RouterItem::Packed(bytes) => {
12761 if let Ok(env) = wire_format::peek_routing_envelope(bytes.as_ref()) {
12762 self.note_side_rx(src, env.ty, bytes.len(), true);
12763 }
12764 }
12765 }
12766 match &item.data {
12767 RouterItem::Packet(pkt) => {
12768 if (is_reliable_type(pkt.data_type()) || !pkt.wire_target_senders().is_empty())
12769 && !is_internal_control_type(pkt.data_type())
12770 && !self.note_reliable_return_route(src, pkt.packet_id())
12771 {
12772 return Ok(());
12773 }
12774 }
12775 RouterItem::Packed(bytes) => {
12776 if let Ok(env) = wire_format::peek_routing_envelope(bytes.as_ref())
12777 && (is_reliable_type(env.ty) || env.target_senders().len() != 0)
12778 && !is_internal_control_type(env.ty)
12779 && let Ok(packet_id) = wire_format::packet_id_from_wire(bytes.as_ref())
12780 && !self.note_reliable_return_route(src, packet_id)
12781 {
12782 return Ok(());
12783 }
12784 }
12785 }
12786 }
12787 match &item.data {
12788 RouterItem::Packet(pkt) => {
12789 if !is_internal_control_type(pkt.data_type()) {
12790 self.remember_managed_variable_packet(pkt)?;
12791 }
12792 }
12793 RouterItem::Packed(bytes) => {
12794 if let Ok(env) = wire_format::peek_routing_envelope(bytes.as_ref())
12795 && !is_internal_control_type(env.ty)
12796 && self.is_managed_variable_type(env.ty)
12797 {
12798 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
12799 pkt.validate()?;
12800 self.remember_managed_variable_packet(&pkt)?;
12801 }
12802 }
12803 }
12804 let mut released_buffered: Vec<crate::SharedBytes> = Vec::new();
12805 if let (Some(src), RouterItem::Packed(bytes)) = (item.src, &item.data) {
12806 let (_opts, handler_is_packed, hop_reliable_enabled) = {
12807 let st = self.state.lock();
12808 let side_ref = Self::side_ref(&st, src)?;
12809 let opts = side_ref.opts.clone();
12810 (
12811 opts.clone(),
12812 matches!(
12813 side_ref.tx_handler,
12814 RouterTxHandlerFn::Packed(_) | RouterTxHandlerFn::PackedWithPriority(_)
12815 ),
12816 opts.reliable_enabled
12817 && self.cfg.reliable_enabled()
12818 && !self.side_has_multiple_announcers_locked(&st, src, self.clock.now_ms()),
12819 )
12820 };
12821
12822 if hop_reliable_enabled && handler_is_packed {
12823 let frame = match wire_format::peek_routing_frame_info(bytes.as_ref()) {
12824 Ok(frame) => frame,
12825 Err(e) => {
12826 if matches!(e, TelemetryError::Unpack(msg) if msg == "crc32 mismatch") {
12827 if let Ok(frame) =
12828 wire_format::peek_frame_info_unchecked(bytes.as_ref())
12829 && is_reliable_type(frame.envelope.ty)
12830 && let Some(hdr) = frame.reliable
12831 {
12832 let unordered =
12833 (hdr.flags & wire_format::RELIABLE_FLAG_UNORDERED) != 0;
12834 let unsequenced =
12835 (hdr.flags & wire_format::RELIABLE_FLAG_UNSEQUENCED) != 0;
12836
12837 if !unsequenced {
12838 let requested = if unordered {
12839 hdr.seq
12840 } else {
12841 let mut st = self.state.lock();
12842 let rx_state = self.reliable_rx_state_mut(
12843 &mut st,
12844 src,
12845 frame.envelope.ty,
12846 );
12847 rx_state.expected_seq.min(hdr.seq)
12848 };
12849 self.queue_reliable_packet_request(
12850 src,
12851 frame.envelope.ty,
12852 requested,
12853 called_from_queue,
12854 )?;
12855 }
12856 }
12857 return Ok(());
12858 }
12859 return Err(e);
12860 }
12861 };
12862 if is_reliable_type(frame.envelope.ty)
12863 && let Some(hdr) = frame.reliable
12864 {
12865 if frame.ack_only() {
12866 self.handle_reliable_ack(src, frame.envelope.ty, hdr.ack);
12867 return Ok(());
12868 }
12869 let unordered = (hdr.flags & wire_format::RELIABLE_FLAG_UNORDERED) != 0;
12870 let unsequenced = (hdr.flags & wire_format::RELIABLE_FLAG_UNSEQUENCED) != 0;
12871
12872 if !unsequenced {
12873 if unordered {
12874 self.queue_reliable_ack(
12875 src,
12876 frame.envelope.ty,
12877 hdr.seq,
12878 called_from_queue,
12879 )?;
12880 } else {
12881 let mut release: Vec<crate::SharedBytes> = Vec::new();
12882 let mut last_delivered = None;
12883 let mut ack_old = None;
12884 let mut request_missing = None;
12885 let mut partial_ack = None;
12886 {
12887 let mut st = self.state.lock();
12888 let rx_state =
12889 self.reliable_rx_state_mut(&mut st, src, frame.envelope.ty);
12890 let expected_seq = rx_state.expected_seq;
12891 if hdr.seq < expected_seq {
12892 ack_old = Some(expected_seq.saturating_sub(1));
12893 } else if hdr.seq > expected_seq {
12894 request_missing = Some(expected_seq);
12895 partial_ack = Some(hdr.seq);
12896 st.buffer_reliable_rx(
12897 src,
12898 frame.envelope.ty,
12899 hdr.seq,
12900 bytes.clone(),
12901 )?;
12902 } else {
12903 release.push(bytes.clone());
12904 last_delivered = Some(hdr.seq);
12905 let mut next_expected = hdr.seq.wrapping_add(1);
12906 while let Some(buf) = rx_state.buffered.remove(&next_expected) {
12907 release.push(buf);
12908 last_delivered = Some(next_expected);
12909 let next = next_expected.wrapping_add(1);
12910 next_expected = if next == 0 { 1 } else { next };
12911 }
12912 rx_state.expected_seq = next_expected;
12913 }
12914 }
12915
12916 if let Some(ack_seq) = ack_old {
12917 self.queue_reliable_ack(
12918 src,
12919 frame.envelope.ty,
12920 ack_seq,
12921 called_from_queue,
12922 )?;
12923 return Ok(());
12924 }
12925 if let Some(request_seq) = request_missing {
12926 if let Some(partial_seq) = partial_ack {
12927 self.queue_reliable_partial_ack(
12928 src,
12929 frame.envelope.ty,
12930 partial_seq,
12931 called_from_queue,
12932 )?;
12933 }
12934 self.queue_reliable_packet_request(
12935 src,
12936 frame.envelope.ty,
12937 request_seq,
12938 called_from_queue,
12939 )?;
12940 return Ok(());
12941 }
12942
12943 if let Some(ack_seq) = last_delivered {
12944 self.queue_reliable_ack(
12945 src,
12946 frame.envelope.ty,
12947 ack_seq,
12948 called_from_queue,
12949 )?;
12950 }
12951
12952 released_buffered.extend(release.into_iter().skip(1));
12953 }
12954 }
12955 }
12956 } else {
12957 match wire_format::peek_routing_frame_info(bytes.as_ref()) {
12958 Ok(frame) => {
12959 if frame.ack_only() {
12960 return Ok(());
12961 }
12962 }
12963 Err(e) => {
12964 if matches!(e, TelemetryError::Unpack(msg) if msg == "crc32 mismatch") {
12965 return Ok(());
12966 }
12967 return Err(e);
12968 }
12969 }
12970 }
12971 }
12972
12973 if self.is_duplicate_pkt(&item.data)? {
12974 if (item.src.is_some()
12977 && self
12978 .item_target_senders(&item.data)?
12979 .iter()
12980 .any(|target| *target != Self::sender_hash(self.sender_arc().as_ref())))
12981 || (item.src.is_some() && self.should_route_remote(&item.data, item.src)? && {
12982 let (endpoints, _) = self.item_route_info(&item.data)?;
12983 self.item_targets_router_as_intermediate_hop(&item.data, &endpoints)?
12984 })
12985 {
12986 let id = Self::get_hash(&item.data);
12987 let replay = {
12988 let mut st = self.state.lock();
12989 st.reliable_return_routes.get_mut(&id).is_some_and(|route| {
12990 let now = self.clock.now_ms();
12991 if now.saturating_sub(route.last_replay_ms)
12992 < runtime_reliable_retransmit_ms()
12993 {
12994 return false;
12995 }
12996 route.last_replay_ms = now;
12997 true
12998 })
12999 };
13000 if replay {
13001 self.relay_send(item.data.clone(), item.src, called_from_queue)?;
13002 }
13003 }
13004 if item.src.is_some() {
13005 let local_sender = self.sender_arc();
13006 match &item.data {
13007 RouterItem::Packet(pkt)
13008 if (is_reliable_type(pkt.data_type())
13009 || !pkt.wire_target_senders().is_empty())
13010 && pkt.sender() != local_sender.as_ref()
13011 && self.item_targets_local_sender(&item.data)?
13012 && self.packet_has_local_delivery(pkt)
13013 && !(self.should_route_remote(&item.data, item.src)?
13014 && self.item_targets_router_as_intermediate_hop(
13015 &item.data,
13016 pkt.endpoints(),
13017 )?) =>
13018 {
13019 self.queue_end_to_end_reliable_ack(pkt, called_from_queue)?;
13020 }
13021 RouterItem::Packed(bytes) => {
13022 if let Ok(pkt) = self.unpack_packet_for_role(bytes.as_ref())
13023 && (is_reliable_type(pkt.data_type())
13024 || !pkt.wire_target_senders().is_empty())
13025 && pkt.sender() != local_sender.as_ref()
13026 && self.item_targets_local_sender(&item.data)?
13027 && self.packet_has_local_delivery(&pkt)
13028 && !(self.should_route_remote(&item.data, item.src)?
13029 && self.item_targets_router_as_intermediate_hop(
13030 &item.data,
13031 pkt.endpoints(),
13032 )?)
13033 {
13034 let packet_id = wire_format::packet_id_from_wire(bytes.as_ref())
13035 .unwrap_or_else(|_| pkt.packet_id());
13036 self.queue_end_to_end_reliable_ack_for_packet_id(
13037 packet_id,
13038 pkt.sender(),
13039 called_from_queue,
13040 )?;
13041 }
13042 }
13043 _ => {}
13044 }
13045 }
13046 return Ok(());
13047 }
13048
13049 self.dispatch_rx_data(item, called_from_queue)?;
13050
13051 for release_bytes in released_buffered {
13052 let release_data = RouterItem::Packed(release_bytes.clone());
13053 if self.is_duplicate_pkt(&release_data)? {
13054 continue;
13055 }
13056 let release_item = RouterRxItem {
13057 src: item.src,
13058 priority: Self::router_item_priority(&release_data)?,
13059 data: release_data,
13060 };
13061 self.dispatch_rx_data(&release_item, called_from_queue)?;
13062 }
13063
13064 Ok(())
13065 }
13066
13067 fn dispatch_rx_data(
13068 &self,
13069 item: &RouterRxItem,
13070 called_from_queue: bool,
13071 ) -> TelemetryResult<()> {
13072 match &item.data {
13073 RouterItem::Packet(pkt) => {
13074 pkt.validate()?;
13075 #[cfg(feature = "discovery")]
13076 if let Some(side) = item.src {
13077 self.refresh_peer_liveness(side, sender_address_u32(pkt.sender()));
13078 }
13079
13080 if self.handle_internal_reliable_packet(pkt, item.src, called_from_queue)? {
13081 return Ok(());
13082 }
13083
13084 if pkt.data_type() == DataType::P2pMessage {
13085 if self.item_targets_local_sender(&item.data)? {
13086 self.dispatch_p2p_packet(pkt)?;
13087 if item.src.is_some() {
13088 self.queue_end_to_end_reliable_ack(pkt, called_from_queue)?;
13089 }
13090 }
13091 if self.should_route_remote(&item.data, item.src)? {
13092 self.relay_send(
13093 RouterItem::Packet(pkt.to_owned()),
13094 item.src,
13095 called_from_queue,
13096 )?;
13097 }
13098 return Ok(());
13099 }
13100
13101 #[cfg(feature = "timesync")]
13102 if matches!(
13103 pkt.data_type(),
13104 DataType::TimeSyncAnnounce
13105 | DataType::TimeSyncRequest
13106 | DataType::TimeSyncResponse
13107 ) {
13108 self.handle_internal_timesync_packet(pkt, item.src, called_from_queue)?;
13109 return Ok(());
13110 }
13111
13112 if self.learn_discovery_packet(pkt, item.src, called_from_queue)? {
13113 if !discovery_is_hop_local_advertisement(pkt.data_type())
13118 && self.should_route_remote(&item.data, item.src)?
13119 {
13120 self.relay_send(
13121 RouterItem::Packet(pkt.to_owned()),
13122 item.src,
13123 called_from_queue,
13124 )?;
13125 }
13126 return Ok(());
13127 }
13128
13129 let mut eps: Vec<DataEndpoint> = pkt.endpoints().to_vec();
13130 crate::small_sort::sort(&mut eps);
13131 eps.dedup();
13132 let had_local_handler = eps.iter().copied().any(|ep| {
13133 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
13134 });
13135 let had_local_delivery =
13136 had_local_handler || self.is_managed_variable_type(pkt.data_type());
13137
13138 let has_remote = self.should_route_remote(&item.data, item.src)?;
13139 let targets_local = self.item_targets_local_sender(&item.data)?;
13140
13141 let has_packed_local = eps
13142 .iter()
13143 .copied()
13144 .any(|ep| self.endpoint_has_packed_handler(ep));
13145 let bytes_opt = if has_packed_local {
13146 Some(wire_format::pack_packet(pkt))
13147 } else {
13148 None
13149 };
13150
13151 if targets_local {
13152 for dest in eps {
13153 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
13154 let result = match (&h.handler, &bytes_opt) {
13155 (EndpointHandlerFn::Packed(_), Some(bytes)) => self
13156 .call_handler_with_retries(
13157 dest,
13158 h,
13159 Some(bytes.as_ref()),
13160 Some(pkt),
13161 None,
13162 called_from_queue,
13163 ),
13164 (EndpointHandlerFn::Packed(_), None) => {
13165 let bytes = wire_format::pack_packet(pkt);
13166 self.call_handler_with_retries(
13167 dest,
13168 h,
13169 Some(bytes.as_ref()),
13170 Some(pkt),
13171 None,
13172 called_from_queue,
13173 )
13174 }
13175 (EndpointHandlerFn::Packet(_), _) => self
13176 .call_handler_with_retries(
13177 dest,
13178 h,
13179 None,
13180 Some(pkt),
13181 None,
13182 called_from_queue,
13183 ),
13184 };
13185 if result.is_err()
13186 && let Some(src) = item.src
13187 {
13188 self.note_side_local_handler_failure(
13189 src,
13190 pkt.data_type(),
13191 runtime_max_handler_retries(),
13192 );
13193 }
13194 }
13195 }
13196 }
13197
13198 if let Some(src) = item.src
13199 && had_local_delivery
13200 && targets_local
13201 {
13202 self.note_side_local_delivery(src, pkt.data_type());
13203 }
13204
13205 if item.src.is_some()
13206 && had_local_delivery
13207 && targets_local
13208 && (is_reliable_type(pkt.data_type()) || !pkt.wire_target_senders().is_empty())
13209 && !(has_remote
13210 && self
13211 .item_targets_router_as_intermediate_hop(&item.data, pkt.endpoints())?)
13212 {
13213 self.queue_end_to_end_reliable_ack(pkt, called_from_queue)?;
13214 }
13215
13216 if has_remote {
13217 let relay_item = RouterItem::Packet(pkt.to_owned());
13218 self.relay_send(relay_item, item.src, called_from_queue)?;
13219 }
13220
13221 Ok(())
13222 }
13223 RouterItem::Packed(bytes) => {
13224 let routing = wire_format::peek_routing_envelope(bytes.as_ref())?;
13228 if !is_internal_control_type(routing.ty)
13229 && !routing.is_reliable
13230 && routing.target_senders().len() == 0
13231 && !self.is_managed_variable_type(routing.ty)
13232 && !routing.endpoints().any(|ep| {
13233 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
13234 })
13235 {
13236 return self.relay_send(item.data.clone(), item.src, called_from_queue);
13240 }
13241
13242 let env = wire_format::peek_envelope(bytes.as_ref())?;
13243
13244 if matches!(
13245 env.ty,
13246 DataType::ReliableAck
13247 | DataType::ReliablePartialAck
13248 | DataType::ReliablePacketRequest
13249 ) {
13250 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13251 pkt.validate()?;
13252 let handled =
13253 self.handle_internal_reliable_packet(&pkt, item.src, called_from_queue)?;
13254 if !handled && self.should_route_remote(&item.data, item.src)? {
13257 self.relay_send(RouterItem::Packet(pkt), item.src, called_from_queue)?;
13258 }
13259 return Ok(());
13260 }
13261
13262 if env.ty == DataType::P2pMessage {
13263 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13264 pkt.validate()?;
13265 if self.item_targets_local_sender(&item.data)? {
13266 self.dispatch_p2p_packet(&pkt)?;
13267 if item.src.is_some() {
13268 let packet_id = wire_format::packet_id_from_wire(bytes.as_ref())
13269 .unwrap_or_else(|_| pkt.packet_id());
13270 self.queue_end_to_end_reliable_ack_for_packet_id(
13271 packet_id,
13272 pkt.sender(),
13273 called_from_queue,
13274 )?;
13275 }
13276 }
13277 if self.should_route_remote(&item.data, item.src)? {
13278 self.relay_send(RouterItem::Packet(pkt), item.src, called_from_queue)?;
13279 }
13280 return Ok(());
13281 }
13282
13283 #[cfg(feature = "timesync")]
13284 if matches!(
13285 env.ty,
13286 DataType::TimeSyncAnnounce
13287 | DataType::TimeSyncRequest
13288 | DataType::TimeSyncResponse
13289 ) {
13290 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13291 pkt.validate()?;
13292 self.handle_internal_timesync_packet(&pkt, item.src, called_from_queue)?;
13293 return Ok(());
13294 }
13295
13296 #[cfg(feature = "discovery")]
13297 if discovery::is_discovery_type(env.ty) {
13298 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13299 pkt.validate()?;
13300 let _ = self.learn_discovery_packet(&pkt, item.src, called_from_queue)?;
13301 if !discovery_is_hop_local_advertisement(env.ty)
13302 && self.should_route_remote(&item.data, item.src)?
13303 {
13304 self.relay_send(RouterItem::Packet(pkt), item.src, called_from_queue)?;
13305 }
13306 return Ok(());
13307 }
13308
13309 let any_packet_needed = env
13310 .endpoints
13311 .iter()
13312 .copied()
13313 .any(|ep| self.endpoint_has_packet_handler(ep));
13314
13315 let mut pkt_opt = if any_packet_needed {
13316 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13317 pkt.validate()?;
13318 Some(pkt)
13319 } else {
13320 None
13321 };
13322
13323 let mut eps: Vec<DataEndpoint> = env.endpoints.iter().copied().collect();
13324 crate::small_sort::sort(&mut eps);
13325 eps.dedup();
13326 let had_local_handler = eps.iter().copied().any(|ep| {
13327 self.endpoint_has_packet_handler(ep) || self.endpoint_has_packed_handler(ep)
13328 });
13329 let had_local_delivery = had_local_handler || self.is_managed_variable_type(env.ty);
13330
13331 let has_remote = self.should_route_remote(&item.data, item.src)?;
13332 let targets_local = self.item_targets_local_sender(&item.data)?;
13333
13334 if targets_local {
13335 for dest in eps {
13336 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
13337 let result = match &h.handler {
13338 EndpointHandlerFn::Packed(_) => self.call_handler_with_retries(
13339 dest,
13340 h,
13341 Some(bytes.as_ref()),
13342 pkt_opt.as_ref(),
13343 Some(&env),
13344 called_from_queue,
13345 ),
13346 EndpointHandlerFn::Packet(_) => {
13347 if pkt_opt.is_none() {
13348 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13349 pkt.validate()?;
13350 pkt_opt = Some(pkt);
13351 }
13352 let pkt_ref = pkt_opt.as_ref().expect("just set");
13353 self.call_handler_with_retries(
13354 dest,
13355 h,
13356 None,
13357 Some(pkt_ref),
13358 Some(&env),
13359 called_from_queue,
13360 )
13361 }
13362 };
13363 if result.is_err()
13364 && let Some(src) = item.src
13365 {
13366 self.note_side_local_handler_failure(
13367 src,
13368 env.ty,
13369 runtime_max_handler_retries(),
13370 );
13371 }
13372 }
13373 }
13374 }
13375
13376 if item.src.is_some()
13377 && had_local_delivery
13378 && targets_local
13379 && (is_reliable_type(env.ty) || !env.target_senders.is_empty())
13380 && !(has_remote
13381 && self
13382 .item_targets_router_as_intermediate_hop(&item.data, &env.endpoints)?)
13383 {
13384 let packet_id =
13385 wire_format::packet_id_from_wire(bytes.as_ref()).or_else(|_| {
13386 pkt_opt
13387 .as_ref()
13388 .map(Packet::packet_id)
13389 .ok_or(TelemetryError::Unpack("missing packet id"))
13390 })?;
13391 self.queue_end_to_end_reliable_ack_for_packet_id(
13392 packet_id,
13393 &env.sender,
13394 called_from_queue,
13395 )?;
13396 }
13397
13398 if has_remote {
13399 let relay_item = match pkt_opt {
13400 Some(ref p) => RouterItem::Packet(p.clone()),
13401 None => RouterItem::Packed(bytes.clone()),
13402 };
13403 self.relay_send(relay_item, item.src, called_from_queue)?;
13404 }
13405
13406 Ok(())
13407 }
13408 }
13409 }
13410
13411 fn dispatch_local_for_item(
13412 &self,
13413 item: &RouterItem,
13414 called_from_queue: bool,
13415 ) -> TelemetryResult<()> {
13416 match item {
13417 RouterItem::Packet(pkt) => {
13418 pkt.validate()?;
13419 if is_internal_control_type(pkt.data_type()) {
13420 return Ok(());
13421 }
13422 self.ensure_e2e_policy_supported_for_type(pkt.data_type())?;
13423 if !self.item_targets_local_sender(item)? {
13424 return Ok(());
13425 }
13426
13427 let mut eps: Vec<DataEndpoint> = pkt.endpoints().to_vec();
13428 crate::small_sort::sort(&mut eps);
13429 eps.dedup();
13430
13431 let has_packed_local = eps
13432 .iter()
13433 .copied()
13434 .any(|ep| self.endpoint_has_packed_handler(ep));
13435 let bytes_opt = if has_packed_local {
13436 Some(wire_format::pack_packet(pkt))
13437 } else {
13438 None
13439 };
13440
13441 for dest in eps {
13442 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
13443 match (&h.handler, &bytes_opt) {
13444 (EndpointHandlerFn::Packed(_), Some(bytes)) => {
13445 self.call_handler_with_retries(
13446 dest,
13447 h,
13448 Some(bytes.as_ref()),
13449 Some(pkt),
13450 None,
13451 called_from_queue,
13452 )?;
13453 }
13454 (EndpointHandlerFn::Packed(_), None) => {
13455 let bytes = wire_format::pack_packet(pkt);
13456 self.call_handler_with_retries(
13457 dest,
13458 h,
13459 Some(bytes.as_ref()),
13460 Some(pkt),
13461 None,
13462 called_from_queue,
13463 )?;
13464 }
13465 (EndpointHandlerFn::Packet(_), _) => {
13466 self.call_handler_with_retries(
13467 dest,
13468 h,
13469 None,
13470 Some(pkt),
13471 None,
13472 called_from_queue,
13473 )?;
13474 }
13475 }
13476 }
13477 }
13478 }
13479 RouterItem::Packed(bytes) => {
13480 let env = wire_format::peek_envelope(bytes.as_ref())?;
13481 if is_internal_control_type(env.ty) {
13482 return Ok(());
13483 }
13484 self.ensure_e2e_policy_supported_for_type(env.ty)?;
13485 if !self.item_targets_local_sender(item)? {
13486 return Ok(());
13487 }
13488
13489 let any_packet_needed = env
13490 .endpoints
13491 .iter()
13492 .copied()
13493 .any(|ep| self.endpoint_has_packet_handler(ep));
13494
13495 let mut pkt_opt = if any_packet_needed {
13496 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13497 pkt.validate()?;
13498 Some(pkt)
13499 } else {
13500 None
13501 };
13502
13503 let mut eps: Vec<DataEndpoint> = env.endpoints.iter().copied().collect();
13504 crate::small_sort::sort(&mut eps);
13505 eps.dedup();
13506
13507 for dest in eps {
13508 for h in self.cfg.handlers.iter().filter(|h| h.endpoint == dest) {
13509 match &h.handler {
13510 EndpointHandlerFn::Packed(_) => {
13511 self.call_handler_with_retries(
13512 dest,
13513 h,
13514 Some(bytes.as_ref()),
13515 pkt_opt.as_ref(),
13516 Some(&env),
13517 called_from_queue,
13518 )?;
13519 }
13520 EndpointHandlerFn::Packet(_) => {
13521 if pkt_opt.is_none() {
13522 let pkt = self.unpack_packet_for_role(bytes.as_ref())?;
13523 pkt.validate()?;
13524 pkt_opt = Some(pkt);
13525 }
13526 let pkt_ref = pkt_opt.as_ref().expect("just set");
13527 self.call_handler_with_retries(
13528 dest,
13529 h,
13530 None,
13531 Some(pkt_ref),
13532 Some(&env),
13533 called_from_queue,
13534 )?;
13535 }
13536 }
13537 }
13538 }
13539 }
13540 }
13541
13542 Ok(())
13543 }
13544
13545 fn tx_item_impl(
13551 &self,
13552 item: RouterTxItem,
13553 ignore_local: bool,
13554 called_from_queue: bool,
13555 ) -> TelemetryResult<()> {
13556 match item {
13557 RouterTxItem::Broadcast(data) => {
13558 self.ensure_e2e_policy_supported_for_type(Self::item_data_type(&data)?)?;
13559 if let RouterItem::Packet(pkt) = &data
13560 && !is_internal_control_type(pkt.data_type())
13561 {
13562 self.remember_managed_variable_packet(pkt)?;
13563 }
13564 #[cfg(feature = "discovery")]
13565 let is_discovery = matches!(&data, RouterItem::Packet(pkt) if discovery::is_discovery_type(pkt.data_type()))
13566 || matches!(&data, RouterItem::Packed(bytes)
13567 if wire_format::peek_envelope(bytes.as_ref())
13568 .map(|env| discovery::is_discovery_type(env.ty))
13569 .unwrap_or(false));
13570 if !ignore_local {
13571 if self.is_duplicate_pkt(&data)? {
13572 return Ok(());
13573 }
13574 #[cfg(feature = "discovery")]
13575 if !is_discovery
13576 && !matches!(&data, RouterItem::Packet(pkt) if is_internal_control_type(pkt.data_type()))
13577 && !matches!(&data, RouterItem::Packed(bytes)
13578 if wire_format::peek_routing_envelope(bytes.as_ref())
13579 .map(|env| is_internal_control_type(env.ty))
13580 .unwrap_or(false))
13581 {
13582 self.dispatch_local_for_item(&data, called_from_queue)?;
13583 }
13584 #[cfg(not(feature = "discovery"))]
13585 if !matches!(&data, RouterItem::Packet(pkt) if is_internal_control_type(pkt.data_type()))
13586 && !matches!(&data, RouterItem::Packed(bytes)
13587 if wire_format::peek_routing_envelope(bytes.as_ref())
13588 .map(|env| is_internal_control_type(env.ty))
13589 .unwrap_or(false))
13590 {
13591 self.dispatch_local_for_item(&data, called_from_queue)?;
13592 }
13593 }
13594
13595 let send_remote = match &data {
13596 RouterItem::Packet(pkt) => {
13597 pkt.validate()?;
13598 self.should_route_remote(&data, None)?
13599 }
13600 RouterItem::Packed(bytes) => {
13601 let _ = wire_format::peek_envelope(bytes.as_ref())?;
13602 self.should_route_remote(&data, None)?
13603 }
13604 };
13605
13606 if !send_remote {
13607 let (endpoints, ty) = self.item_route_info(&data)?;
13608 if !ignore_local
13609 && !is_internal_control_type(ty)
13610 && is_reliable_type(ty)
13611 && has_nonlocal_endpoint(&endpoints, &self.cfg)
13612 && !self
13613 .state
13614 .lock()
13615 .managed_variable_types
13616 .contains(&ty.as_u32())
13617 {
13618 self.remove_pkt_id(&data);
13622 return Err(TelemetryError::Io("no discovered route"));
13623 }
13624 return Ok(());
13625 }
13626 let mut data = data;
13627 let ty = match &data {
13628 RouterItem::Packet(pkt) => pkt.data_type(),
13629 RouterItem::Packed(bytes) => {
13630 wire_format::peek_routing_envelope(bytes.as_ref())?.ty
13631 }
13632 };
13633 if !ignore_local
13634 && !is_internal_control_type(ty)
13635 && self.item_requires_end_to_end_tracking(&data, ty)?
13636 {
13637 #[cfg(feature = "discovery")]
13638 {
13639 let pending = {
13640 let st = self.state.lock();
13641 let mut pending = self
13642 .expected_end_to_end_destinations_locked(&st, &data, None, false)?;
13643 self.filter_trackable_end_to_end_destinations_locked(
13644 &st,
13645 ty,
13646 &mut pending,
13647 false,
13648 );
13649 pending
13650 };
13651 if !pending.is_empty() {
13652 let original_data = data.clone();
13653 let mut targets: Vec<u64> = pending.keys().copied().collect();
13654 crate::small_sort::sort(&mut targets);
13655 targets.dedup();
13656 data = self.attach_wire_contract_to_item(data, &targets)?;
13657 match self.register_end_to_end_reliable_tx(&data, Some(pending)) {
13658 Ok(false) => return Ok(()),
13659 Ok(true) => {}
13660 Err(error) => {
13661 self.remove_pkt_id(&original_data);
13662 return Err(error);
13663 }
13664 }
13665 }
13666 }
13667 }
13668 let RemoteSidePlan::Target(sides) = self.remote_side_plan(&data, None)?;
13669 for side in sides.iter().copied() {
13670 if let Err(e) = self.send_reliable_to_side(side, data.clone(), false) {
13671 if Self::is_side_tx_busy(&e) {
13672 self.tx_queue_item_for_retry(
13673 RouterTxItem::ToSide {
13674 src: None,
13675 dst: side,
13676 data: data.clone(),
13677 },
13678 true,
13679 )?;
13680 continue;
13681 }
13682 match &data {
13683 RouterItem::Packet(pkt) => {
13684 let _ = self.handle_callback_error(pkt, None, e, called_from_queue);
13685 }
13686 RouterItem::Packed(bytes) => {
13687 if let Ok(env) = wire_format::peek_envelope(bytes.as_ref()) {
13688 let _ = self.handle_callback_error_from_env(
13689 &env,
13690 None,
13691 e,
13692 called_from_queue,
13693 );
13694 }
13695 }
13696 }
13697 return Err(TelemetryError::HandlerError("tx handler failed"));
13698 }
13699 }
13700 }
13701 RouterTxItem::ToSide { src, dst, data } => {
13702 self.ensure_e2e_policy_supported_for_type(Self::item_data_type(&data)?)?;
13703 if let RouterItem::Packet(pkt) = &data
13704 && !is_internal_control_type(pkt.data_type())
13705 {
13706 self.remember_managed_variable_packet(pkt)?;
13707 }
13708 if !ignore_local {
13709 if self.is_duplicate_pkt(&data)? {
13710 return Ok(());
13711 }
13712 let suppress_local = matches!(&data, RouterItem::Packet(pkt) if is_internal_control_type(pkt.data_type()))
13713 || matches!(&data, RouterItem::Packed(bytes)
13714 if wire_format::peek_routing_envelope(bytes.as_ref())
13715 .map(|env| is_internal_control_type(env.ty))
13716 .unwrap_or(false));
13717 if !suppress_local {
13718 self.dispatch_local_for_item(&data, called_from_queue)?;
13719 }
13720 }
13721 let allowed = {
13722 let mut st = self.state.lock();
13723 let ty = match &data {
13724 RouterItem::Packet(pkt) => Some(pkt.data_type()),
13725 RouterItem::Packed(bytes) => {
13726 Some(wire_format::peek_routing_envelope(bytes.as_ref())?.ty)
13727 }
13728 };
13729 let route_allowed = self.route_allowed_locked(&st, src, ty, dst);
13730 #[cfg(all(feature = "discovery", feature = "timesync"))]
13731 let timesync_allowed = ty
13732 .map(|ty| {
13733 Self::timesync_allowed_for_side_locked(
13734 &mut st,
13735 dst,
13736 ty,
13737 self.clock.now_ms(),
13738 )
13739 })
13740 .unwrap_or(true);
13741 #[cfg(not(all(feature = "discovery", feature = "timesync")))]
13742 let timesync_allowed = true;
13743 route_allowed && timesync_allowed
13744 };
13745 if !allowed {
13746 return Ok(());
13747 }
13748 if let Err(e) = self.send_reliable_to_side(dst, data.clone(), src.is_some()) {
13749 if Self::is_side_tx_busy(&e) {
13750 self.tx_queue_item_for_retry(
13751 RouterTxItem::ToSide { src, dst, data },
13752 true,
13753 )?;
13754 return Ok(());
13755 }
13756 match &data {
13757 RouterItem::Packet(pkt) => {
13758 let _ = self.handle_callback_error(pkt, None, e, called_from_queue);
13759 }
13760 RouterItem::Packed(bytes) => {
13761 if let Ok(env) = wire_format::peek_envelope(bytes.as_ref()) {
13762 let _ = self.handle_callback_error_from_env(
13763 &env,
13764 None,
13765 e,
13766 called_from_queue,
13767 );
13768 }
13769 }
13770 }
13771 return Err(TelemetryError::HandlerError("tx handler failed"));
13772 }
13773 }
13774 RouterTxItem::EndToEndReplay { packet_id } => {
13775 let Some((data, mut sides)) = self.end_to_end_retransmit_sides(packet_id) else {
13776 return Ok(());
13777 };
13778 if sides.is_empty() {
13779 let RemoteSidePlan::Target(fallback_sides) =
13780 self.remote_side_plan(&data, None)?;
13781 sides = fallback_sides;
13782 }
13783 for side in sides.iter().copied() {
13784 if let Err(e) = self.send_reliable_to_side(side, data.clone(), false) {
13785 if Self::is_side_tx_busy(&e) {
13786 self.tx_queue_item_for_retry(
13787 RouterTxItem::ToSide {
13788 src: None,
13789 dst: side,
13790 data: data.clone(),
13791 },
13792 true,
13793 )?;
13794 continue;
13795 }
13796 match &data {
13797 RouterItem::Packet(pkt) => {
13798 let _ = self.handle_callback_error(pkt, None, e, called_from_queue);
13799 }
13800 RouterItem::Packed(bytes) => {
13801 if let Ok(env) = wire_format::peek_envelope(bytes.as_ref()) {
13802 let _ = self.handle_callback_error_from_env(
13803 &env,
13804 None,
13805 e,
13806 called_from_queue,
13807 );
13808 }
13809 }
13810 }
13811 return Err(TelemetryError::HandlerError("tx handler failed"));
13812 }
13813 }
13814 }
13815 RouterTxItem::ReliableReplay { dst, bytes } => {
13816 let frame = wire_format::peek_frame_info(bytes.as_ref())?;
13817 let ty = frame.envelope.ty;
13818 let Some(hdr) = frame.reliable else {
13819 return Ok(());
13820 };
13821 {
13822 let mut st = self.state.lock();
13823 let tx_state = self.reliable_tx_state_mut(&mut st, dst, ty);
13824 if !tx_state.sent.contains_key(&hdr.seq) {
13825 return Ok(());
13826 }
13827 }
13828 if let Err(e) = self.send_reliable_raw_to_side(dst, bytes.clone(), false) {
13829 if Self::is_side_tx_busy(&e) {
13830 self.tx_queue_item_for_retry(
13831 RouterTxItem::ReliableReplay { dst, bytes },
13832 true,
13833 )?;
13834 return Ok(());
13835 }
13836 return Err(e);
13837 }
13838 let mut st = self.state.lock();
13839 let tx_state = self.reliable_tx_state_mut(&mut st, dst, ty);
13840 if let Some(sent) = tx_state.sent.get_mut(&hdr.seq) {
13841 sent.last_send_ms = self.clock.now_ms();
13842 sent.queued = false;
13843 }
13844 }
13845 }
13846
13847 Ok(())
13848 }
13849
13850 #[inline]
13852 fn tx_item(&self, item: RouterTxItem) -> TelemetryResult<()> {
13853 self.tx_item_impl(item, false, false)
13854 }
13855
13856 #[inline]
13863 pub fn rx_packed(&self, bytes: &[u8]) -> TelemetryResult<()> {
13864 if self.side_tx_active() {
13865 return self.rx_packed_queue(bytes);
13866 }
13867 crate::memory_admission::check_receive(bytes)?;
13868 let data = RouterItem::Packed(crate::shared_bytes::convert(Arc::<[u8]>::from(bytes)));
13869 let item = RouterRxItem {
13870 src: None,
13871 priority: Self::router_item_priority(&data)?,
13872 data,
13873 };
13874 self.rx_item(&item, false)
13875 }
13876
13877 #[inline]
13882 pub fn rx(&self, pkt: &Packet) -> TelemetryResult<()> {
13883 if self.side_tx_active() {
13884 return self.rx_queue(pkt.clone());
13885 }
13886 let data = RouterItem::Packet(pkt.clone());
13887 let item = RouterRxItem {
13888 src: None,
13889 priority: Self::router_item_priority(&data)?,
13890 data,
13891 };
13892 self.rx_item(&item, false)
13893 }
13894
13895 #[inline]
13900 pub fn rx_from_side(&self, pkt: &Packet, side: RouterSideId) -> TelemetryResult<()> {
13901 if self.side_tx_active() {
13902 return self.rx_queue_from_side(pkt.clone(), side);
13903 }
13904 self.ensure_side_ingress_enabled(side)?;
13905 let data = RouterItem::Packet(pkt.clone());
13906 let item = RouterRxItem {
13907 src: Some(side),
13908 priority: Self::router_item_priority(&data)?,
13909 data,
13910 };
13911 self.rx_item(&item, false)
13912 }
13913
13914 #[inline]
13919 pub fn rx_packed_from_side(&self, bytes: &[u8], side: RouterSideId) -> TelemetryResult<()> {
13920 if self.side_tx_active() {
13921 return self.rx_packed_queue_from_side(bytes, side);
13922 }
13923 self.ensure_side_ingress_enabled(side)?;
13924 let Some(decoded) = self.decode_side_transport_frame(side, bytes)? else {
13925 return Ok(());
13926 };
13927 let data = RouterItem::Packed(decoded);
13928 let item = RouterRxItem {
13929 src: Some(side),
13930 priority: Self::router_item_priority(&data)?,
13931 data,
13932 };
13933 self.rx_item(&item, false)
13934 }
13935
13936 #[inline]
13943 pub fn tx(&self, pkt: Packet) -> TelemetryResult<()> {
13944 #[cfg(feature = "discovery")]
13945 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13946 if self.side_tx_active() {
13947 return self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)));
13948 }
13949 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
13950 }
13951
13952 #[inline]
13957 pub fn tx_packed(&self, pkt: Arc<[u8]>) -> TelemetryResult<()> {
13958 #[cfg(feature = "discovery")]
13959 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13960 if self.side_tx_active() {
13961 return self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packed(
13962 crate::shared_bytes::convert(pkt),
13963 )));
13964 }
13965 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packed(
13966 crate::shared_bytes::convert(pkt),
13967 )))
13968 }
13969
13970 #[inline]
13976 pub fn tx_queue(&self, pkt: Packet) -> TelemetryResult<()> {
13977 self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
13978 }
13979
13980 #[inline]
13982 pub fn tx_packed_queue(&self, data: Arc<[u8]>) -> TelemetryResult<()> {
13983 self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packed(
13984 crate::shared_bytes::convert(data),
13985 )))
13986 }
13987
13988 #[inline]
13995 pub fn log<T: LeBytes>(&self, ty: DataType, data: &[T]) -> TelemetryResult<()> {
13996 #[cfg(feature = "discovery")]
13997 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
13998 if self.side_tx_active() {
13999 return self.log_queue(ty, data);
14000 }
14001 let sender = self.sender_arc();
14002 log_raw(
14003 sender.as_ref(),
14004 ty,
14005 data,
14006 self.packet_timestamp_ms(),
14007 |pkt| self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt))),
14008 )
14009 }
14010
14011 #[inline]
14013 pub fn log_queue<T: LeBytes>(&self, ty: DataType, data: &[T]) -> TelemetryResult<()> {
14014 let sender = self.sender_arc();
14015 log_raw(
14016 sender.as_ref(),
14017 ty,
14018 data,
14019 self.packet_timestamp_ms(),
14020 |pkt| self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt))),
14021 )
14022 }
14023
14024 #[inline]
14026 pub fn log_ts<T: LeBytes>(
14027 &self,
14028 ty: DataType,
14029 timestamp: u64,
14030 data: &[T],
14031 ) -> TelemetryResult<()> {
14032 #[cfg(feature = "discovery")]
14033 let _ = crate::memory_admission::allow_drain(self.poll_discovery())?;
14034 if self.side_tx_active() {
14035 return self.log_queue_ts(ty, timestamp, data);
14036 }
14037 let sender = self.sender_arc();
14038 log_raw(sender.as_ref(), ty, data, timestamp, |pkt| {
14039 self.tx_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
14040 })
14041 }
14042
14043 #[inline]
14045 pub fn log_queue_ts<T: LeBytes>(
14046 &self,
14047 ty: DataType,
14048 timestamp: u64,
14049 data: &[T],
14050 ) -> TelemetryResult<()> {
14051 let sender = self.sender_arc();
14052 log_raw(sender.as_ref(), ty, data, timestamp, |pkt| {
14053 self.tx_queue_item(RouterTxItem::Broadcast(RouterItem::Packet(pkt)))
14054 })
14055 }
14056}
14057
14058#[cfg(all(test, feature = "std", feature = "discovery"))]
14059mod queued_schema_identity_tests {
14060 use super::*;
14061
14062 fn catalog(timestamp: u64, byte: u8) -> Packet {
14063 Packet::new(
14064 crate::DataType::DiscoverySchema,
14065 &[DataEndpoint::Discovery],
14066 "SCHEMA_PEER",
14067 timestamp,
14068 crate::shared_bytes::convert(Arc::<[u8]>::from(vec![byte; 1024])),
14069 )
14070 .unwrap()
14071 }
14072
14073 #[test]
14074 fn repeated_schema_preserves_partial_transfer_but_updates_and_other_sides_do_not_coalesce() {
14075 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
14076 let side = router.add_side_packed("A", |_| Ok(()));
14077 let other_side = router.add_side_packed("B", |_| Ok(()));
14078 let original = catalog(1, 42);
14079 let duplicate = catalog(2, 42);
14080 assert_ne!(original.packet_id(), duplicate.packet_id());
14081 let mut st = router.state.lock();
14082 st.transmit_queue.clear();
14083 let queued = |dst, pkt| TxQueued {
14084 item: RouterTxItem::ToSide {
14085 src: None,
14086 dst,
14087 data: RouterItem::Packet(pkt),
14088 },
14089 ignore_local: true,
14090 priority: 255,
14091 retry_after_ms: 123,
14092 };
14093 st.push_transmit(queued(side, original.clone())).unwrap();
14094 st.push_transmit(queued(side, duplicate.clone())).unwrap();
14095 assert_eq!(st.transmit_queue.len(), 1);
14096 let pending = st.transmit_queue.iter().next().unwrap();
14097 assert_eq!(pending.retry_after_ms, 123);
14098 assert!(
14099 matches!(&pending.item, RouterTxItem::ToSide { data: RouterItem::Packet(pkt), .. }
14100 if pkt.packet_id() == original.packet_id())
14101 );
14102 st.push_transmit(queued(other_side, duplicate)).unwrap();
14103 assert_eq!(st.transmit_queue.len(), 2);
14104 let changed = catalog(3, 43);
14105 st.push_transmit(queued(side, changed.clone())).unwrap();
14106 assert_eq!(st.transmit_queue.len(), 2);
14107 assert!(st.transmit_queue.iter().any(|queued| matches!(&queued.item,
14108 RouterTxItem::ToSide { dst, data: RouterItem::Packet(pkt), .. }
14109 if *dst == side && pkt.packet_id() == changed.packet_id())));
14110 }
14111
14112 #[test]
14113 fn repeated_announcements_complete_one_fragment_per_dispatch() {
14114 use core::sync::atomic::{AtomicUsize, Ordering};
14115 let allowance = Arc::new(AtomicUsize::new(0));
14116 let accepted = Arc::new(std::sync::Mutex::new(Vec::<Arc<[u8]>>::new()));
14117 let available = allowance.clone();
14118 let frames = accepted.clone();
14119 let node = Router::new_with_clock(RouterConfig::default(), Box::new(|| 1u64));
14120 let side = node.add_side_packed_with_options(
14121 "bounded CAN",
14122 move |bytes| {
14123 if available
14124 .try_update(Ordering::Relaxed, Ordering::Relaxed, |count| {
14125 count.checked_sub(1)
14126 })
14127 .is_err()
14128 {
14129 return Err(TelemetryError::Io("side tx busy"));
14130 }
14131 frames.lock().unwrap().push(Arc::<[u8]>::from(bytes));
14132 Ok(())
14133 },
14134 RouterSideOptions::default().with_small_packet_transport(128),
14135 );
14136 let handler = node.state.lock().sides[side]
14137 .as_ref()
14138 .unwrap()
14139 .tx_handler
14140 .clone();
14141 let mut complete = false;
14142 for timestamp in 1..100 {
14143 let mut state = 0x517cc1b7u32;
14145 let payload: Vec<u8> = (0..3600)
14146 .map(|_| {
14147 state ^= state << 13;
14148 state ^= state >> 17;
14149 state ^= state << 5;
14150 state as u8
14151 })
14152 .collect();
14153 let packet = Packet::new(
14154 crate::DataType::DiscoverySchema,
14155 &[crate::DataEndpoint::Discovery],
14156 "SCHEMA_PEER",
14157 timestamp,
14158 crate::shared_bytes::convert(Arc::<[u8]>::from(payload)),
14159 )
14160 .unwrap();
14161 node.state
14162 .lock()
14163 .push_transmit(TxQueued {
14164 item: RouterTxItem::ToSide {
14165 src: None,
14166 dst: side,
14167 data: RouterItem::Packet(packet),
14168 },
14169 ignore_local: true,
14170 priority: 255,
14171 retry_after_ms: 0,
14172 })
14173 .unwrap();
14174 let data = match &node.state.lock().transmit_queue.iter().next().unwrap().item {
14175 RouterTxItem::ToSide { data, .. } => data.clone(),
14176 _ => panic!("unexpected item"),
14177 };
14178 allowance.store(1, Ordering::Relaxed);
14179 match node.call_side_tx_handler(side, &handler, &data, false) {
14180 Ok(()) => {
14181 complete = true;
14182 break;
14183 }
14184 Err(TelemetryError::Io(_)) => {}
14185 other => panic!("unexpected result: {other:?}"),
14186 }
14187 }
14188 assert!(
14189 complete,
14190 "new announcement timestamps must not replay the prefix forever"
14191 );
14192 let received = accepted.lock().unwrap();
14193 assert!(
14196 received.len() > 1,
14197 "fixture must exercise fragmentation, not a compressed single frame"
14198 );
14199 let first = &received[0];
14200 assert_eq!(&first[..4], &[83, 68, 84, 3]);
14201 assert_eq!(
14202 u16::from_le_bytes([first[10], first[11]]) as usize,
14203 received.len()
14204 );
14205 let transfer_id = &first[4..8];
14206 for (index, frame) in received.iter().enumerate() {
14207 assert_eq!(&frame[4..8], transfer_id);
14208 assert_eq!(u16::from_le_bytes([frame[8], frame[9]]) as usize, index);
14209 }
14210 }
14211 #[test]
14212 fn schema_receipt_resolves_assigned_numeric_address_without_adding_peers() {
14213 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
14214 let side = router.add_side_packed("A", |_| Ok(()));
14215 let assigned = 0x1234u32;
14216 assert_ne!(sender_address_u32("SCHEMA_PEER"), assigned);
14217 {
14218 let mut st = router.state.lock();
14219 st.discovery_routes
14220 .entry(side)
14221 .or_default()
14222 .announcers
14223 .insert(
14224 "SCHEMA_PEER".into(),
14225 DiscoverySenderState {
14226 advertised_address: Some(assigned),
14227 ..DiscoverySenderState::default()
14228 },
14229 );
14230 }
14231 router.note_schema_received(side, "@addr:4660");
14232 router.note_schema_received(side, "@addr:4661");
14233 let st = router.state.lock();
14234 let peers = &st.discovery_routes.get(&side).unwrap().announcers;
14235 assert_eq!(peers.len(), 1);
14236 assert!(peers["SCHEMA_PEER"].has_schema);
14237 }
14238 #[test]
14239 fn busy_handler_returns_after_one_attempt_and_other_errors_keep_retry_budget() {
14240 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
14241 let attempts = core::cell::Cell::new(0);
14242 let result: Result<((), usize), _> = router.retry_with_attempts(8, || {
14243 attempts.set(attempts.get() + 1);
14244 Err(TelemetryError::Io("side tx busy"))
14245 });
14246 assert!(matches!(
14247 result,
14248 Err((TelemetryError::Io("side tx busy"), 1))
14249 ));
14250 assert_eq!(attempts.get(), 1);
14251 attempts.set(0);
14252 let result: Result<((), usize), _> = router.retry_with_attempts(3, || {
14253 attempts.set(attempts.get() + 1);
14254 Err(TelemetryError::Io("permanent handler failure"))
14255 });
14256 assert!(matches!(
14257 result,
14258 Err((TelemetryError::Io("permanent handler failure"), 3))
14259 ));
14260 assert_eq!(attempts.get(), 3);
14261 }
14262 #[test]
14263 fn rejected_first_template_frame_is_retried_self_describing() {
14264 crate::tests::ensure_common_test_schema();
14265 let ty = crate::config::register_data_type_with_description(
14266 "ROUTER_BUSY_FIRST_TEMPLATE",
14267 "",
14268 crate::MessageElement::Static(
14269 1,
14270 crate::MessageDataType::UInt8,
14271 crate::MessageClass::Data,
14272 ),
14273 &[crate::DataEndpoint::named("RADIO")],
14274 crate::ReliableMode::None,
14275 1,
14276 )
14277 .unwrap();
14278 let tx = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
14279 let rx = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0u64));
14280 let opts = RouterSideOptions::default().with_small_packet_transport(0);
14281 let a = tx.add_side_packed_with_options("wire", |_| Ok(()), opts.clone());
14282 let b = rx.add_side_packed_with_options("wire", |_| Ok(()), opts.clone());
14283 let packet = Packet::new(
14284 ty,
14285 &[crate::DataEndpoint::named("RADIO")],
14286 "BUSY_PEER",
14287 1,
14288 crate::shared_bytes::convert(Arc::<[u8]>::from([42u8])),
14289 )
14290 .unwrap();
14291 let raw = wire_format::pack_packet(&packet);
14292 let first = tx
14294 .encode_side_transport_frames(
14295 a,
14296 opts.clone(),
14297 crate::shared_bytes::convert(raw.clone()),
14298 )
14299 .unwrap();
14300 assert!(
14301 tx.send_side_frames_resumable(a, first, |_| Err(TelemetryError::Io("side tx busy")))
14302 .is_err()
14303 );
14304 let retry = tx
14305 .encode_side_transport_frames(
14306 a,
14307 opts.clone(),
14308 crate::shared_bytes::convert(raw.clone()),
14309 )
14310 .unwrap();
14311 let mut received = None;
14312 tx.send_side_frames_resumable(a, retry, |frame| {
14313 assert_eq!(
14314 frame[3], SIDE_TRANSPORT_KIND_FULL,
14315 "retry requires the unaccepted dictionary bootstrap"
14316 );
14317 received = rx.decode_side_transport_frame(b, frame)?;
14318 Ok(())
14319 })
14320 .unwrap();
14321 assert_eq!(received, Some(crate::shared_bytes::convert(raw.clone())));
14322 let next = tx
14324 .encode_side_transport_frames(a, opts, crate::shared_bytes::convert(raw.clone()))
14325 .unwrap();
14326 tx.send_side_frames_resumable(a, next, |frame| {
14327 assert_eq!(frame[3], SIDE_TRANSPORT_KIND_COMPACT);
14328 assert_eq!(
14329 rx.decode_side_transport_frame(b, frame)?,
14330 Some(crate::shared_bytes::convert(raw.clone()))
14331 );
14332 Ok(())
14333 })
14334 .unwrap();
14335 }
14336 #[test]
14337 fn already_queued_receipt_precedes_ordered_retry_exhaustion() {
14338 crate::tests::ensure_common_test_schema();
14339 let ep = DataEndpoint::named("RADIO");
14340 let ty = crate::config::register_data_type_with_description(
14341 "QUEUED_RECEIPT_DEADLINE",
14342 "",
14343 MessageElement::Static(1, crate::MessageDataType::UInt8, crate::MessageClass::Data),
14344 &[ep],
14345 crate::ReliableMode::Ordered,
14346 1,
14347 )
14348 .unwrap();
14349 for isr in [false, true] {
14350 let now = Arc::new(core::sync::atomic::AtomicU64::new(0));
14351 let clock = now.clone();
14352 let router = Router::new_with_clock(
14353 RouterConfig::new([]).with_hostname("FC"),
14354 Box::new(move || clock.load(Ordering::Relaxed)),
14355 );
14356 let side = router.add_side_packed("CAN", |_| Ok(()));
14357 let packet = Packet::new(ty, &[ep], "FC", 1, Arc::<[u8]>::from([42u8])).unwrap();
14358 let id = packet.packet_id();
14359 let owner = Router::sender_hash("GS");
14360 let mut destinations = BTreeMap::new();
14361 destinations.insert(owner, side);
14362 router.state.lock().end_to_end_reliable_tx.insert(
14363 id,
14364 EndToEndReliableSent {
14365 ordered_type: Some(ty.as_u32()),
14366 waiting_for_predecessor: false,
14367 data: RouterItem::Packet(packet),
14368 pending_destinations: destinations,
14369 tracked_destinations: true,
14370 last_send_ms: 0,
14371 retries: runtime_reliable_max_retries(),
14372 queued: false,
14373 },
14374 );
14375 let snapshot = router.export_reliable_pending_stats();
14376 assert_eq!(snapshot.len(), 1);
14377 assert_eq!(snapshot[0].packet_id, id);
14378 assert_eq!(snapshot[0].data_type, ty);
14379 assert_eq!(snapshot[0].pending_destinations, vec![owner]);
14380 assert_eq!(snapshot[0].retries, runtime_reliable_max_retries());
14381 assert!(!snapshot[0].queued);
14382 assert!(!snapshot[0].waiting_for_predecessor);
14383 let ack = Packet::new_with_wire_contract(
14384 DataType::ReliableAck,
14385 message_meta(DataType::ReliableAck).endpoints_ref(),
14386 "E2EACK:GS",
14387 1,
14388 1,
14389 Arc::<[u8]>::from(id.to_le_bytes()),
14390 None,
14391 Arc::from([owner, Router::sender_hash("FC")]),
14392 )
14393 .unwrap();
14394 let packed = wire_format::pack_packet(&ack);
14395 if isr {
14396 router.rx_packed_queue_from_side_isr(&packed, side).unwrap();
14397 } else {
14398 router.rx_packed_queue_from_side(&packed, side).unwrap();
14399 }
14400 now.store(runtime_reliable_retransmit_ms(), Ordering::Relaxed);
14401 router
14402 .drain_queues_with_timeout(1)
14403 .expect("a received ACK must be processed before cancelling its ordered delivery");
14404 assert!(!router.state.lock().end_to_end_reliable_tx.contains_key(&id));
14405 assert!(router.export_reliable_pending_stats().is_empty());
14406 }
14407 }
14408 #[test]
14409 fn embedded_gateway_receipt_with_numeric_sender_clears_host_pending_owner() {
14410 crate::tests::ensure_common_test_schema();
14411 let bytes = [
14414 0x0c, 0x01, 0x01, 0x08, 0xd4, 0xe5, 0xea, 0xb6, 0xb7, 0x33, 0xff, 0x90, 0x02, 0xb4,
14415 0xe6, 0xdd, 0x94, 0x07, 0x14, 0x03, 0x44, 0x02, 0x02, 0x06, 0x25, 0xd2, 0x59, 0x1b,
14416 0xbb, 0xfc, 0xa7, 0x5f, 0x43, 0x92, 0x9e, 0x12, 0xbb, 0xfc, 0xa7, 0x26, 0xa2, 0xa0,
14417 0xbe, 0x39, 0x65, 0xe2, 0xd8, 0x61, 0x68, 0x22, 0xfd,
14418 ];
14419 let ack = wire_format::unpack_packet(&bytes).unwrap();
14420 let owner = Router::sender_hash("GB");
14421 assert_eq!(Router::end_to_end_ack_sender_hash(&ack), Some(owner));
14422 let id = Router::decode_end_to_end_reliable_ack(ack.payload()).unwrap();
14423 let router = Router::new(RouterConfig::new([]).with_hostname("GS"));
14424 let side = router.add_side_packed("Pico", |_| Ok(()));
14425 let data = ack.clone();
14426 router.state.lock().end_to_end_reliable_tx.insert(
14427 id,
14428 EndToEndReliableSent {
14429 ordered_type: None,
14430 waiting_for_predecessor: false,
14431 data: RouterItem::Packet(data),
14432 pending_destinations: BTreeMap::from([(owner, side)]),
14433 tracked_destinations: true,
14434 last_send_ms: 0,
14435 retries: 0,
14436 queued: false,
14437 },
14438 );
14439 router.rx_packed_queue_from_side(&bytes, side).unwrap();
14440 router.drain_queues_with_timeout(1).unwrap();
14441 assert!(router.export_reliable_pending_stats().is_empty());
14442 }
14443
14444 #[test]
14445 fn frozen_delivery_contract_bypasses_dictionary_even_for_best_effort_schema() {
14446 crate::tests::ensure_common_test_schema();
14447 let ep = DataEndpoint::named("RADIO");
14448 let ty = crate::config::register_data_type_with_description(
14449 "MANAGED_CONTROL_WITH_FROZEN_OWNERS",
14450 "",
14451 MessageElement::Static(1, crate::MessageDataType::UInt8, crate::MessageClass::Data),
14452 &[ep],
14453 crate::ReliableMode::None,
14454 16,
14455 )
14456 .unwrap();
14457 let router = Router::new(RouterConfig::new([]).with_hostname("GS"));
14458 let opts = RouterSideOptions::default().with_template_transport();
14459 let side = router.add_side_packed_with_options("radio", |_| Ok(()), opts.clone());
14460 let packet = Packet::new_with_wire_contract(
14461 ty,
14462 &[ep],
14463 "GS",
14464 1,
14465 1,
14466 Arc::<[u8]>::from([1u8]),
14467 None,
14468 Arc::from([Router::sender_hash("RF")]),
14469 )
14470 .unwrap();
14471 let raw = wire_format::pack_packet(&packet);
14472 assert_eq!(
14473 router
14474 .item_service_priority(&RouterItem::Packet(packet.clone()))
14475 .unwrap(),
14476 254
14477 );
14478 assert_eq!(
14479 router
14480 .item_service_priority(&RouterItem::Packed(crate::shared_bytes::convert(
14481 raw.clone()
14482 )))
14483 .unwrap(),
14484 254
14485 );
14486 assert!(
14487 !wire_format::peek_routing_frame_info(&raw)
14488 .unwrap()
14489 .envelope
14490 .is_reliable
14491 );
14492 for _ in 0..3 {
14493 let frames = router
14494 .encode_side_transport_frames(
14495 side,
14496 opts.clone(),
14497 crate::shared_bytes::convert(raw.clone()),
14498 )
14499 .unwrap();
14500 let mut count = 0;
14501 router
14502 .send_side_frames_resumable(side, frames, |bytes| {
14503 assert_eq!(
14504 bytes,
14505 raw.as_ref(),
14506 "delivery contract must remain independently decodable"
14507 );
14508 count += 1;
14509 Ok(())
14510 })
14511 .unwrap();
14512 assert_eq!(count, 1);
14513 }
14514 }
14515
14516 #[test]
14517 fn managed_receive_queues_keep_service_priority_with_low_schema_priority() {
14518 crate::tests::ensure_common_test_schema();
14519 let ep = DataEndpoint::named("RADIO");
14520 let ty = crate::config::register_data_type_with_description(
14521 "MANAGED_RX_SERVICE_PRIORITY",
14522 "",
14523 MessageElement::Static(1, crate::MessageDataType::UInt8, crate::MessageClass::Data),
14524 &[ep],
14525 crate::ReliableMode::None,
14526 16,
14527 )
14528 .unwrap();
14529 let router = Router::new(RouterConfig::new([]).with_hostname("RF"));
14530 router
14531 .enable_network_variable(ty, NetworkVariablePermissions::READ_ONLY)
14532 .unwrap();
14533 let side = router.add_side_packed("radio", |_| Ok(()));
14534 router.clear_queues();
14535 let packet = Packet::new(ty, &[ep], "GS", 1, Arc::<[u8]>::from([1])).unwrap();
14536 let wire = wire_format::pack_packet(&packet);
14537 router.rx_queue(packet.clone()).unwrap();
14538 router.rx_queue_from_side(packet.clone(), side).unwrap();
14539 router.rx_packed_queue(&wire).unwrap();
14540 router.rx_packed_queue_from_side(&wire, side).unwrap();
14541 for _ in 0..4 {
14542 let queued = router
14543 .pop_next_queued_control_rx_item()
14544 .unwrap()
14545 .expect("registered managed controls must precede queued bulk work");
14546 assert_eq!(
14547 queued.priority,
14548 crate::transport_priority(DataType::ManagedVariableValue)
14549 );
14550 }
14551 assert!(router.pop_next_queued_control_rx_item().unwrap().is_none());
14552 }
14553
14554 #[test]
14555 fn managed_value_keeps_service_priority_after_side_backpressure() {
14556 crate::tests::ensure_common_test_schema();
14557 let ep = DataEndpoint::named("RADIO");
14558 let ty = crate::config::register_data_type_with_description(
14559 "MANAGED_RETRY_SERVICE_PRIORITY",
14560 "",
14561 MessageElement::Static(1, crate::MessageDataType::UInt8, crate::MessageClass::Data),
14562 &[ep],
14563 crate::ReliableMode::None,
14564 16,
14565 )
14566 .unwrap();
14567 let router = Router::new(RouterConfig::new([]).with_hostname("GS"));
14568 router
14569 .enable_network_variable(ty, NetworkVariablePermissions::READ_WRITE)
14570 .unwrap();
14571 let side = router.add_side_packed("radio", |_| Err(TelemetryError::Io("side tx busy")));
14572 let packet = Packet::new(ty, &[ep], "GS", 1, Arc::<[u8]>::from([1])).unwrap();
14573 let item = RouterItem::Packet(packet);
14574 for queued in [
14575 RouterTxItem::Broadcast(item.clone()),
14576 RouterTxItem::ToSide {
14577 src: None,
14578 dst: side,
14579 data: item.clone(),
14580 },
14581 ] {
14582 router.tx_queue_item_for_retry(queued, true).unwrap();
14583 let queued = router.state.lock().transmit_queue.pop_front().unwrap();
14584 assert_eq!(
14585 queued.priority,
14586 crate::transport_priority(DataType::ManagedVariableValue),
14587 "a busy side must not demote managed controls to bulk application priority"
14588 );
14589 assert!(queued.retry_after_ms > 0);
14590 }
14591 }
14592 #[test]
14593 fn receipt_budget_does_not_starve_transmit() {
14594 use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
14595 crate::tests::ensure_common_test_schema();
14596 let now = Arc::new(AtomicU64::new(0));
14597 let clock = now.clone();
14598 let count = Arc::new(AtomicUsize::new(0));
14599 let accepted = count.clone();
14600 let node = Router::new_with_clock(
14601 RouterConfig::default(),
14602 Box::new(move || clock.fetch_add(1, Ordering::Relaxed)),
14603 );
14604 let side = node.add_side_packed("wire", move |_| {
14605 accepted.fetch_add(1, Ordering::Relaxed);
14606 Ok(())
14607 });
14608 node.clear_queues();
14609 let packet = Packet::new(
14610 crate::DataType::ReliableAck,
14611 &[crate::DataEndpoint::TelemetryError],
14612 "E2EACK:PEER",
14613 1,
14614 Arc::<[u8]>::from([0u8; 8]),
14615 )
14616 .unwrap();
14617 node.state
14618 .lock()
14619 .push_transmit(TxQueued {
14620 item: RouterTxItem::ToSide {
14621 src: None,
14622 dst: side,
14623 data: RouterItem::Packet(packet.clone()),
14624 },
14625 ignore_local: true,
14626 priority: 250,
14627 retry_after_ms: 0,
14628 })
14629 .unwrap();
14630 node.rx_packed_queue_from_side(
14631 &wire_format::pack_packet(
14632 &Packet::new(
14633 crate::DataType::ReliableAck,
14634 &[crate::DataEndpoint::TelemetryError],
14635 "RX_PEER",
14636 2,
14637 Arc::<[u8]>::from([0u8; 8]),
14638 )
14639 .unwrap(),
14640 ),
14641 side,
14642 )
14643 .unwrap();
14644 node.drain_queues_with_timeout(1).unwrap();
14645 assert_eq!(
14646 count.load(Ordering::Relaxed),
14647 1,
14648 "control RX exhausting its cooperative budget must still allow one queued TX"
14649 );
14650 }
14651}
14652
14653#[cfg(all(test, feature = "discovery", feature = "std"))]
14654fn untemplated_test_node() -> Router {
14655 Router::new_with_clock(RouterConfig::default(), Box::new(|| 0))
14656}
14657#[cfg(all(test, feature = "discovery", feature = "std"))]
14658fn untemplated_test_options() -> RouterSideOptions {
14659 RouterSideOptions::default().with_small_packet_transport(64)
14660}
14661#[cfg(all(test, feature = "discovery", feature = "std"))]
14662#[allow(clippy::duplicate_mod)]
14663#[path = "tests/untemplated_transport.rs"]
14664mod untemplated_transport_tests;
14665
14666#[cfg(all(test, feature = "std", feature = "discovery"))]
14667mod queued_producer_maintenance_tests {
14668 use super::*;
14669 #[test]
14670 fn queued_publication_defers_discovery_to_worker_maintenance() {
14671 crate::tests::ensure_common_test_schema();
14672 let ty = DataType::named("GPS_DATA");
14673 for variant in 0..4 {
14674 let sent = Arc::new(std::sync::Mutex::new(Vec::new()));
14675 let output = sent.clone();
14676 let router = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0));
14677 router.add_side_packed("wire", move |bytes| {
14678 output
14679 .lock()
14680 .unwrap()
14681 .push(wire_format::unpack_packet(bytes)?.data_type());
14682 Ok(())
14683 });
14684 router.clear_queues();
14685 let packet =
14686 Packet::from_f32_slice(ty, &[1.0, 2.0, 3.0], message_meta(ty).endpoints_ref(), 42)
14687 .unwrap();
14688 match variant {
14689 0 => router.tx_queue(packet).unwrap(),
14690 1 => router
14691 .tx_packed_queue(wire_format::pack_packet(&packet))
14692 .unwrap(),
14693 2 => router.log_queue(ty, &[1.0f32, 2.0, 3.0]).unwrap(),
14694 _ => router.log_queue_ts(ty, 42, &[1.0f32, 2.0, 3.0]).unwrap(),
14695 }
14696 assert!(
14697 sent.lock().unwrap().is_empty(),
14698 "producer invoked a transport"
14699 );
14700 assert_eq!(
14701 router.state.lock().transmit_queue.len(),
14702 1,
14703 "queued API performed discovery on the producer stack (variant {variant})"
14704 );
14705 router.periodic(0).unwrap();
14706 let sent = sent.lock().unwrap();
14707 assert!(sent.contains(&ty));
14708 assert!(
14709 sent.iter().copied().any(discovery::is_discovery_type),
14710 "worker maintenance must still announce the board"
14711 );
14712 }
14713 }
14714}
14715
14716#[cfg(all(test, feature = "std"))]
14717mod bounded_fair_queue_tests {
14718 use super::*;
14719 #[test]
14720 fn small_canonical_reports_survive_fast_senders_fixed_ring_bursts() {
14721 let ep = crate::config::register_endpoint("ROUTER_SMALL_QUEUE_DEST", false).unwrap();
14722 let ty = crate::config::register_data_type_with_description(
14723 "ROUTER_SMALL_QUEUE_REPORT",
14724 "",
14725 crate::MessageElement::Static(
14726 4,
14727 crate::MessageDataType::UInt8,
14728 crate::MessageClass::Data,
14729 ),
14730 &[ep],
14731 crate::ReliableMode::None,
14732 0,
14733 )
14734 .unwrap();
14735 for tx in [false, true] {
14736 for slow_last in [false, true] {
14737 let node = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0));
14738 let make = |sender| {
14739 RouterItem::Packed(crate::shared_bytes::convert(wire_format::pack_packet(
14740 &Packet::new(ty, &[ep], sender, 1, Arc::<[u8]>::from([1; 4])).unwrap(),
14741 )))
14742 };
14743 let rx = |data| RouterRxItem {
14744 src: None,
14745 data,
14746 priority: 0,
14747 };
14748 let tx_item = |data| TxQueued {
14749 item: RouterTxItem::Broadcast(data),
14750 ignore_local: true,
14751 priority: 0,
14752 retry_after_ms: 0,
14753 };
14754 let mut st = node.state.lock();
14755 st.memory.max_queue_budget = usize::MAX;
14756 if tx {
14757 st.transmit_queue = BoundedDeque::new(8192, 3 * size_of::<TxQueued>(), 1.0);
14758 } else {
14759 st.received_queue = BoundedDeque::new(8192, 3 * size_of::<RouterRxItem>(), 1.0);
14760 }
14761 let order = if slow_last {
14762 ["FAST", "FAST", "SLOW"]
14763 } else {
14764 ["SLOW", "FAST", "FAST"]
14765 };
14766 for sender in order.into_iter().chain(core::iter::repeat_n("FAST", 32)) {
14767 if tx {
14768 st.push_transmit(tx_item(make(sender))).unwrap();
14769 } else {
14770 st.push_received(rx(make(sender))).unwrap();
14771 }
14772 }
14773 let slow = wire_format::source_address_for_sender("SLOW");
14774 let retained = if tx {
14775 st.transmit_queue
14776 .iter()
14777 .any(|item| RouterInner::tx_bulk_owner(item) == Some(slow))
14778 } else {
14779 st.received_queue.iter().any(|item| {
14780 RouterInner::fair_bulk_owner(&item.data, item.priority) == Some(slow)
14781 })
14782 };
14783 assert!(
14784 retained,
14785 "small report was displaced: tx={tx}, slow_last={slow_last}"
14786 );
14787 }
14788 }
14789 }
14790
14791 #[test]
14792 fn slow_stream_survives_same_board_fast_stream_bursts() {
14793 let ep = crate::config::register_endpoint("ROUTER_STREAM_QUEUE_DEST", false).unwrap();
14794 let ty = crate::config::register_data_type_with_description(
14795 "ROUTER_STREAM_QUEUE_FAST",
14796 "",
14797 crate::MessageElement::Static(
14798 64,
14799 crate::MessageDataType::UInt8,
14800 crate::MessageClass::Data,
14801 ),
14802 &[ep],
14803 crate::ReliableMode::None,
14804 0,
14805 )
14806 .unwrap();
14807 let slow_ty = crate::config::register_data_type_with_description(
14808 "ROUTER_STREAM_QUEUE_SLOW",
14809 "",
14810 crate::MessageElement::Static(
14811 64,
14812 crate::MessageDataType::UInt8,
14813 crate::MessageClass::Data,
14814 ),
14815 &[ep],
14816 crate::ReliableMode::None,
14817 0,
14818 )
14819 .unwrap();
14820 for mode in 0..3 {
14821 for tx in [false, true] {
14822 for slow_last in [false, true] {
14823 let node = Router::new_with_clock(RouterConfig::default(), Box::new(|| 0));
14824 let make = |sender| {
14825 let packet = Packet::new(
14826 if sender == "SLOW" { slow_ty } else { ty },
14827 &[ep],
14828 "SAME_BOARD",
14829 1,
14830 Arc::<[u8]>::from([1; 64]),
14831 )
14832 .unwrap();
14833 if mode == 1 {
14834 RouterItem::Packet(packet)
14835 } else {
14836 RouterItem::Packed(crate::shared_bytes::convert(
14837 wire_format::pack_packet(&packet),
14838 ))
14839 }
14840 };
14841 let rx = |data| RouterRxItem {
14842 src: None,
14843 data,
14844 priority: 0,
14845 };
14846 let tx_item = |data| TxQueued {
14847 item: RouterTxItem::Broadcast(data),
14848 ignore_local: true,
14849 priority: 0,
14850 retry_after_ms: 0,
14851 };
14852 let mut st = node.state.lock();
14853 st.memory.max_queue_budget = usize::MAX;
14854 if mode == 0 {
14855 if tx {
14856 st.transmit_queue =
14857 BoundedDeque::new(8192, 3 * size_of::<TxQueued>(), 1.0);
14858 } else {
14859 st.received_queue =
14860 BoundedDeque::new(8192, 3 * size_of::<RouterRxItem>(), 1.0);
14861 }
14862 } else if mode == 1 {
14863 let cost = if tx {
14864 tx_item(make("SLOW")).byte_cost()
14865 + 2 * tx_item(make("FAST")).byte_cost()
14866 } else {
14867 rx(make("SLOW")).byte_cost() + 2 * rx(make("FAST")).byte_cost()
14868 };
14869 if tx {
14870 st.transmit_queue = BoundedDeque::new(cost, cost, 2.0);
14871 } else {
14872 st.received_queue = BoundedDeque::new(cost, cost, 2.0);
14873 }
14874 }
14875 let order = if slow_last {
14876 ["FAST", "FAST", "SLOW"]
14877 } else {
14878 ["SLOW", "FAST", "FAST"]
14879 };
14880 for (index, sender) in order
14881 .into_iter()
14882 .chain(core::iter::repeat_n("FAST", 32))
14883 .enumerate()
14884 {
14885 if mode == 2 && index == 3 {
14886 st.memory.max_queue_budget = st.shared_queue_bytes_used();
14887 }
14888 if tx {
14889 st.push_transmit(tx_item(make(sender))).unwrap();
14890 } else {
14891 st.push_received(rx(make(sender))).unwrap();
14892 }
14893 }
14894 let is_slow = |data: &RouterItem| match data {
14895 RouterItem::Packet(p) => p.data_type() == slow_ty,
14896 RouterItem::Packed(b) => {
14897 wire_format::scheduling_type(b.as_ref()) == Some(slow_ty)
14898 }
14899 };
14900 let retained = if tx {
14901 st.transmit_queue.iter().any(|item| {
14902 let data = match &item.item {
14903 RouterTxItem::Broadcast(data)
14904 | RouterTxItem::ToSide { data, .. } => data,
14905 _ => return false,
14906 };
14907 is_slow(data)
14908 })
14909 } else {
14910 st.received_queue.iter().any(|item| is_slow(&item.data))
14911 };
14912 assert!(
14913 retained,
14914 "slow stream displaced: mode={mode}, tx={tx}, slow_last={slow_last}"
14915 );
14916 }
14917 }
14918 }
14919 }
14920
14921 #[test]
14922 fn fast_producer_preserves_slow_reports_at_each_queue_boundary() {
14923 let ep = crate::config::register_endpoint("QUEUE_FAIR_DEST", false).unwrap();
14924 let ty = crate::config::register_data_type_with_description(
14925 "QUEUE_FAIR_REPORT",
14926 "",
14927 MessageElement::Static(64, crate::MessageDataType::UInt8, crate::MessageClass::Data),
14928 &[ep],
14929 crate::ReliableMode::None,
14930 0,
14931 )
14932 .unwrap();
14933 for mode in 0..3 {
14934 for tx in [false, true] {
14935 for slow_last in [false, true] {
14936 for next_sender in ["FAST", "NEW_SLOW"] {
14937 let router =
14938 Router::new_with_clock(RouterConfig::default(), Box::new(|| 0));
14939 let make = |sender| {
14940 RouterItem::Packet(
14941 Packet::new(ty, &[ep], sender, 1, Arc::<[u8]>::from([1; 64]))
14942 .unwrap(),
14943 )
14944 };
14945 let rx = |data| RouterRxItem {
14946 src: None,
14947 data,
14948 priority: 0,
14949 };
14950 let tx_item = |data| TxQueued {
14951 item: RouterTxItem::Broadcast(data),
14952 ignore_local: true,
14953 priority: 0,
14954 retry_after_ms: 0,
14955 };
14956 let order = if slow_last {
14957 ["FAST", "FAST", "SLOW"]
14958 } else {
14959 ["SLOW", "FAST", "FAST"]
14960 };
14961 let mut st = router.state.lock();
14962 st.memory.max_queue_budget = usize::MAX;
14963 let cost = if tx {
14964 order.iter().map(|s| tx_item(make(s)).byte_cost()).sum()
14965 } else {
14966 order.iter().map(|s| rx(make(s)).byte_cost()).sum()
14967 };
14968 if mode == 0 {
14969 if tx {
14970 st.transmit_queue = BoundedDeque::new(cost, cost, 2.0);
14971 } else {
14972 st.received_queue = BoundedDeque::new(cost, cost, 2.0);
14973 }
14974 } else if mode == 1 {
14975 if tx {
14976 st.transmit_queue =
14977 BoundedDeque::new(cost * 10, 3 * size_of::<TxQueued>(), 1.0);
14978 } else {
14979 st.received_queue = BoundedDeque::new(
14980 cost * 10,
14981 3 * size_of::<RouterRxItem>(),
14982 1.0,
14983 );
14984 }
14985 }
14986 for sender in order {
14987 if tx {
14988 st.push_transmit(tx_item(make(sender))).unwrap();
14989 } else {
14990 st.push_received(rx(make(sender))).unwrap();
14991 }
14992 }
14993 if mode == 2 {
14994 st.memory.max_queue_budget = st.shared_queue_bytes_used();
14995 }
14996 if tx {
14997 st.push_transmit(tx_item(make(next_sender))).unwrap_or_else(|error| panic!("admit error {error:?}: mode={mode} tx={tx} slow_last={slow_last} incoming={next_sender}"));
14998 } else {
14999 st.push_received(rx(make(next_sender))).unwrap_or_else(|error| panic!("admit error {error:?}: mode={mode} tx={tx} slow_last={slow_last} incoming={next_sender}"));
15000 }
15001 let slow = wire_format::source_address_for_sender("SLOW");
15002 let retained = if tx {
15003 st.transmit_queue
15004 .iter()
15005 .any(|item| RouterInner::tx_bulk_owner(item) == Some(slow))
15006 } else {
15007 st.received_queue.iter().any(|item| {
15008 RouterInner::fair_bulk_owner(&item.data, item.priority)
15009 == Some(slow)
15010 })
15011 };
15012 assert!(
15013 retained,
15014 "slow report evicted: mode={mode} tx={tx} slow_last={slow_last} incoming={next_sender}"
15015 );
15016 }
15017 }
15018 }
15019 }
15020 }
15021}