1use super::*;
2use crate::transport::PacketBuffer;
3use std::ops::Range;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub(crate) struct FipsEndpointDirectPacketRunMeta {
9 source_peer: PeerIdentity,
10 previous_hop_addr: NodeAddr,
11 received_k_bit: bool,
12 direct_path: bool,
13 enqueued_at_ms: u64,
14}
15
16impl FipsEndpointDirectPacketRunMeta {
17 pub(crate) fn new(
18 source_peer: PeerIdentity,
19 previous_hop_addr: NodeAddr,
20 received_k_bit: bool,
21 direct_path: bool,
22 enqueued_at_ms: u64,
23 ) -> Self {
24 Self {
25 source_peer,
26 previous_hop_addr,
27 received_k_bit,
28 direct_path,
29 enqueued_at_ms,
30 }
31 }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36struct FipsEndpointDirectPacketSegment {
37 buffer: Arc<PacketBuffer>,
38 ranges: Vec<Range<usize>>,
39 packet_bytes: usize,
40}
41
42impl FipsEndpointDirectPacketSegment {
43 fn empty() -> Self {
44 Self::new(PacketBuffer::new(Vec::new()), Vec::new())
45 }
46
47 fn new(buffer: PacketBuffer, ranges: Vec<Range<usize>>) -> Self {
48 Self::from_shared_buffer(Arc::new(buffer), ranges)
49 }
50
51 fn from_shared_buffer(buffer: Arc<PacketBuffer>, ranges: Vec<Range<usize>>) -> Self {
52 debug_assert!(ranges.windows(2).all(|pair| pair[0].end <= pair[1].start));
53 let packet_bytes = ranges.iter().map(|range| range.len()).sum();
54 Self {
55 buffer,
56 ranges,
57 packet_bytes,
58 }
59 }
60
61 fn len(&self) -> usize {
62 self.ranges.len()
63 }
64
65 fn is_empty(&self) -> bool {
66 self.ranges.is_empty()
67 }
68
69 fn split_off(&mut self, at: usize) -> Option<Self> {
70 if at >= self.ranges.len() {
71 return None;
72 }
73 let original_packet_bytes = self.packet_bytes;
74 let tail_ranges = self.ranges.split_off(at);
75 self.packet_bytes = self.ranges.iter().map(|range| range.len()).sum();
76 let tail_packet_bytes = original_packet_bytes.saturating_sub(self.packet_bytes);
77 Some(Self {
78 buffer: Arc::clone(&self.buffer),
79 ranges: tail_ranges,
80 packet_bytes: tail_packet_bytes,
81 })
82 }
83
84 fn retain_ranges<F>(&mut self, next_index: &mut usize, keep: &mut F) -> bool
85 where
86 F: FnMut(usize, &[u8]) -> bool,
87 {
88 let bytes = self.buffer.as_slice();
89 let original_len = self.ranges.len();
90 let mut write = 0usize;
91 let mut packet_bytes = 0usize;
92
93 for read in 0..original_len {
94 let range = self.ranges[read].clone();
95 let current_index = *next_index;
96 *next_index = next_index.saturating_add(1);
97 if !keep(current_index, &bytes[range.clone()]) {
98 continue;
99 }
100
101 packet_bytes = packet_bytes.saturating_add(range.len());
102 if write != read {
103 self.ranges[write] = range;
104 }
105 write = write.saturating_add(1);
106 }
107
108 let changed = write != original_len;
109 if changed {
110 self.ranges.truncate(write);
111 }
112 self.packet_bytes = packet_bytes;
113 changed
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
119enum FipsEndpointDirectPacketStorage {
120 Segmented(FipsEndpointDirectPacketSegment),
121 Chained {
122 segments: Vec<FipsEndpointDirectPacketSegment>,
123 packet_ends: Vec<usize>,
124 packet_bytes: usize,
125 },
126}
127
128impl FipsEndpointDirectPacketStorage {
129 fn empty_segmented() -> Self {
130 Self::Segmented(FipsEndpointDirectPacketSegment::empty())
131 }
132
133 fn push_segment(&mut self, segment: FipsEndpointDirectPacketSegment) {
134 if segment.is_empty() {
135 return;
136 }
137
138 match self {
139 Self::Segmented(current) if current.is_empty() => {
140 *current = segment;
141 }
142 Self::Segmented(current) => {
143 let first = std::mem::replace(current, FipsEndpointDirectPacketSegment::empty());
144 let first_count = first.len();
145 let second_count = first_count.saturating_add(segment.len());
146 let packet_bytes = first.packet_bytes.saturating_add(segment.packet_bytes);
147 *self = Self::Chained {
148 segments: vec![first, segment],
149 packet_ends: vec![first_count, second_count],
150 packet_bytes,
151 };
152 }
153 Self::Chained {
154 segments,
155 packet_ends,
156 packet_bytes,
157 } => {
158 let packet_count = packet_ends
159 .last()
160 .copied()
161 .unwrap_or(0)
162 .saturating_add(segment.len());
163 *packet_bytes = packet_bytes.saturating_add(segment.packet_bytes);
164 packet_ends.push(packet_count);
165 segments.push(segment);
166 }
167 }
168 }
169
170 fn append_storage(&mut self, other: Self) {
171 match other {
172 Self::Segmented(segment) => self.push_segment(segment),
173 Self::Chained { segments, .. } => {
174 for segment in segments {
175 self.push_segment(segment);
176 }
177 }
178 }
179 }
180
181 fn packet_count(&self) -> usize {
182 match self {
183 Self::Segmented(segment) => segment.len(),
184 Self::Chained { packet_ends, .. } => packet_ends.last().copied().unwrap_or(0),
185 }
186 }
187
188 fn split_off_packets(&mut self, at: usize) -> Option<Self> {
189 if at >= self.packet_count() {
190 return None;
191 }
192 if at == 0 {
193 return Some(std::mem::replace(self, Self::empty_segmented()));
194 }
195
196 match self {
197 Self::Segmented(segment) => segment.split_off(at).map(Self::Segmented),
198 Self::Chained {
199 segments,
200 packet_ends,
201 packet_bytes,
202 } => {
203 let original_packet_bytes = *packet_bytes;
204 let segment_index = packet_ends.partition_point(|end| *end <= at);
205 let previous_end = segment_index
206 .checked_sub(1)
207 .and_then(|index| packet_ends.get(index).copied())
208 .unwrap_or(0);
209 let split_in_segment = at - previous_end;
210 let mut tail_segments;
211 let mut tail_packet_ends;
212
213 if split_in_segment == 0 {
214 tail_segments = segments.split_off(segment_index);
215 tail_packet_ends = packet_ends.split_off(segment_index);
216 for end in &mut tail_packet_ends {
217 *end = end.saturating_sub(at);
218 }
219 } else {
220 let original_segment_end = packet_ends[segment_index];
221 tail_segments = segments.split_off(segment_index + 1);
222 let tail_segment = segments[segment_index]
223 .split_off(split_in_segment)
224 .expect("split point inside segment must produce a tail");
225 tail_segments.insert(0, tail_segment);
226
227 tail_packet_ends = packet_ends.split_off(segment_index + 1);
228 if let Some(head_end) = packet_ends.last_mut() {
229 *head_end = at;
230 }
231 for end in &mut tail_packet_ends {
232 *end = end.saturating_sub(at);
233 }
234 tail_packet_ends.insert(0, original_segment_end - at);
235 }
236
237 *packet_bytes = segments.iter().map(|segment| segment.packet_bytes).sum();
238 let tail_packet_bytes = original_packet_bytes.saturating_sub(*packet_bytes);
239 Some(Self::build_chained_from_parts(
240 tail_segments,
241 tail_packet_ends,
242 tail_packet_bytes,
243 ))
244 }
245 }
246 }
247
248 fn build_chained_from_parts(
249 mut segments: Vec<FipsEndpointDirectPacketSegment>,
250 packet_ends: Vec<usize>,
251 packet_bytes: usize,
252 ) -> Self {
253 match segments.len() {
254 0 => Self::empty_segmented(),
255 1 => Self::Segmented(segments.pop().expect("one segment must exist")),
256 _ => Self::Chained {
257 segments,
258 packet_ends,
259 packet_bytes,
260 },
261 }
262 }
263}
264
265#[derive(Debug, Clone, PartialEq, Eq)]
273pub struct FipsEndpointDirectPacketRun {
274 meta: FipsEndpointDirectPacketRunMeta,
275 storage: FipsEndpointDirectPacketStorage,
276}
277
278pub struct FipsEndpointDirectPacketSlices<'a> {
280 storage: &'a FipsEndpointDirectPacketStorage,
281 index: usize,
282 segment_index: usize,
283 segment_packet_index: usize,
284 remaining: usize,
285}
286
287impl FipsEndpointDirectPacketRun {
288 pub(crate) fn from_segmented_payload(
289 meta: FipsEndpointDirectPacketRunMeta,
290 buffer: PacketBuffer,
291 ranges: Vec<Range<usize>>,
292 ) -> Self {
293 Self {
294 meta,
295 storage: FipsEndpointDirectPacketStorage::Segmented(
296 FipsEndpointDirectPacketSegment::new(buffer, ranges),
297 ),
298 }
299 }
300
301 pub fn source_peer(&self) -> &PeerIdentity {
303 &self.meta.source_peer
304 }
305
306 pub fn enqueued_at_ms(&self) -> u64 {
308 self.meta.enqueued_at_ms
309 }
310
311 pub fn len(&self) -> usize {
313 self.storage.packet_count()
314 }
315
316 pub fn is_empty(&self) -> bool {
318 self.len() == 0
319 }
320
321 pub fn packet_bytes(&self) -> usize {
323 match &self.storage {
324 FipsEndpointDirectPacketStorage::Segmented(segment) => segment.packet_bytes,
325 FipsEndpointDirectPacketStorage::Chained { packet_bytes, .. } => *packet_bytes,
326 }
327 }
328
329 pub fn packet_slice(&self, index: usize) -> Option<&[u8]> {
331 match &self.storage {
332 FipsEndpointDirectPacketStorage::Segmented(segment) => segment
333 .ranges
334 .get(index)
335 .map(|range| &segment.buffer.as_slice()[range.clone()]),
336 FipsEndpointDirectPacketStorage::Chained {
337 segments,
338 packet_ends,
339 ..
340 } => {
341 let segment_index = packet_ends.partition_point(|end| *end <= index);
342 let previous_end = segment_index
343 .checked_sub(1)
344 .and_then(|previous| packet_ends.get(previous).copied())
345 .unwrap_or(0);
346 segments.get(segment_index).and_then(|segment| {
347 segment
348 .ranges
349 .get(index - previous_end)
350 .map(|range| &segment.buffer.as_slice()[range.clone()])
351 })
352 }
353 }
354 }
355
356 pub(crate) fn append_run(&mut self, other: FipsEndpointDirectPacketRun) {
357 debug_assert!(self.matches_append_meta(&other));
358 self.storage.append_storage(other.storage);
359 }
360
361 pub(crate) fn matches_append_meta(&self, other: &Self) -> bool {
362 self.meta.source_peer == other.meta.source_peer
363 && self.meta.previous_hop_addr == other.meta.previous_hop_addr
364 && self.meta.received_k_bit == other.meta.received_k_bit
365 && self.meta.direct_path == other.meta.direct_path
366 }
367
368 pub fn packet_slices(&self) -> FipsEndpointDirectPacketSlices<'_> {
370 FipsEndpointDirectPacketSlices {
371 storage: &self.storage,
372 index: 0,
373 segment_index: 0,
374 segment_packet_index: 0,
375 remaining: self.len(),
376 }
377 }
378
379 pub fn retain_packets<F>(&mut self, mut keep: F)
385 where
386 F: FnMut(usize, &[u8]) -> bool,
387 {
388 match &mut self.storage {
389 FipsEndpointDirectPacketStorage::Segmented(segment) => {
390 let mut index = 0usize;
391 segment.retain_ranges(&mut index, &mut keep);
392 }
393 FipsEndpointDirectPacketStorage::Chained {
394 segments,
395 packet_ends,
396 packet_bytes,
397 } => {
398 let mut index = 0usize;
399 let mut retained_bytes = 0usize;
400 let mut changed = false;
401 for segment in segments.iter_mut() {
402 changed |= segment.retain_ranges(&mut index, &mut keep);
403 retained_bytes = retained_bytes.saturating_add(segment.packet_bytes);
404 }
405 if changed {
406 segments.retain(|segment| !segment.is_empty());
407 packet_ends.clear();
408 let mut packet_count = 0usize;
409 for segment in segments.iter() {
410 packet_count = packet_count.saturating_add(segment.len());
411 packet_ends.push(packet_count);
412 }
413 *packet_bytes = retained_bytes;
414 }
415 }
416 }
417 }
418
419 pub fn split_off_packets(&mut self, at: usize) -> Option<Self> {
424 let storage = self.storage.split_off_packets(at)?;
425 Some(Self {
426 meta: self.meta.clone(),
427 storage,
428 })
429 }
430}
431
432impl<'a> Iterator for FipsEndpointDirectPacketSlices<'a> {
433 type Item = &'a [u8];
434
435 fn next(&mut self) -> Option<Self::Item> {
436 if self.remaining == 0 {
437 return None;
438 }
439 let packet = match self.storage {
440 FipsEndpointDirectPacketStorage::Segmented(segment) => segment
441 .ranges
442 .get(self.index)
443 .map(|range| &segment.buffer.as_slice()[range.clone()]),
444 FipsEndpointDirectPacketStorage::Chained { segments, .. } => loop {
445 let Some(segment) = segments.get(self.segment_index) else {
446 break None;
447 };
448 if self.segment_packet_index < segment.len() {
449 let packet = segment
450 .ranges
451 .get(self.segment_packet_index)
452 .map(|range| &segment.buffer.as_slice()[range.clone()]);
453 self.segment_packet_index = self.segment_packet_index.saturating_add(1);
454 if self.segment_packet_index >= segment.len() {
455 self.segment_index = self.segment_index.saturating_add(1);
456 self.segment_packet_index = 0;
457 }
458 break packet;
459 }
460 self.segment_index = self.segment_index.saturating_add(1);
461 self.segment_packet_index = 0;
462 },
463 };
464 if packet.is_some() {
465 self.index = self.index.saturating_add(1);
466 self.remaining = self.remaining.saturating_sub(1);
467 }
468 packet
469 }
470
471 fn size_hint(&self) -> (usize, Option<usize>) {
472 (self.remaining, Some(self.remaining))
473 }
474}
475
476impl ExactSizeIterator for FipsEndpointDirectPacketSlices<'_> {}
477
478#[derive(Debug, Clone, PartialEq, Eq)]
480pub struct FipsEndpointDirectPacketBatch {
481 packet_runs: Vec<FipsEndpointDirectPacketRun>,
482}
483
484impl FipsEndpointDirectPacketBatch {
485 pub(crate) fn from_packet_runs(packet_runs: Vec<FipsEndpointDirectPacketRun>) -> Self {
486 Self { packet_runs }
487 }
488
489 pub fn into_packet_runs(self) -> Vec<FipsEndpointDirectPacketRun> {
491 self.packet_runs
492 }
493}
494
495#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
497pub enum FipsEndpointDirectDeliveryError {
498 #[error("direct endpoint sink unavailable")]
500 Unavailable,
501}
502
503pub trait FipsEndpointDirectSink: Send + Sync + 'static {
508 fn deliver_endpoint_packet_batch(
510 &self,
511 batch: FipsEndpointDirectPacketBatch,
512 ) -> Result<(), FipsEndpointDirectDeliveryError>;
513}
514
515impl<F> FipsEndpointDirectSink for F
516where
517 F: Fn(FipsEndpointDirectPacketBatch) -> Result<(), FipsEndpointDirectDeliveryError>
518 + Send
519 + Sync
520 + 'static,
521{
522 fn deliver_endpoint_packet_batch(
523 &self,
524 batch: FipsEndpointDirectPacketBatch,
525 ) -> Result<(), FipsEndpointDirectDeliveryError> {
526 self(batch)
527 }
528}
529
530#[derive(Clone)]
531pub(crate) struct EndpointDirectSink {
532 sink: Arc<dyn FipsEndpointDirectSink>,
533}
534
535impl std::fmt::Debug for EndpointDirectSink {
536 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
537 f.debug_struct("EndpointDirectSink").finish_non_exhaustive()
538 }
539}
540
541impl EndpointDirectSink {
542 pub(crate) fn new<S>(sink: S) -> Self
543 where
544 S: FipsEndpointDirectSink,
545 {
546 Self {
547 sink: Arc::new(sink),
548 }
549 }
550
551 pub(crate) fn deliver_direct_packet_batch(
552 &self,
553 batch: FipsEndpointDirectPacketBatch,
554 ) -> Result<(), FipsEndpointDirectDeliveryError> {
555 self.sink.deliver_endpoint_packet_batch(batch)
556 }
557}
558
559#[derive(Debug)]
561pub struct ExternalPacketIo {
562 pub outbound_tx: crate::upper::tun::TunOutboundTx,
564 pub inbound_rx: tokio::sync::mpsc::Receiver<NodeDeliveredPacket>,
566}
567
568#[derive(Debug)]
570pub(crate) struct EndpointDataIo {
571 pub(crate) control_tx: tokio::sync::mpsc::Sender<NodeEndpointControlCommand>,
574 pub(crate) data_batch_tx: EndpointDataBatchTx,
580 pub(crate) event_rx: EndpointEventReceiver,
588 pub(crate) event_tx: EndpointEventSender,
592 pub(crate) service_event_rx: EndpointServiceEventReceiver,
594 pub(crate) service_event_tx: EndpointServiceEventSender,
596}
597
598#[derive(Debug, Clone)]
600pub(crate) struct EndpointEventSender {
601 tx: tokio::sync::mpsc::Sender<NodeEndpointEvent>,
602 direct_sink: Option<EndpointDirectSink>,
603 queued_messages: Arc<AtomicUsize>,
604 ready: Arc<EndpointEventReady>,
605 message_cap: usize,
606}
607
608#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
609pub(crate) enum EndpointEventSendError {
610 #[error("endpoint event channel closed")]
611 Closed,
612}
613
614#[derive(Debug)]
615pub(crate) struct EndpointEventReceiver {
616 rx: tokio::sync::mpsc::Receiver<NodeEndpointEvent>,
617 queued_messages: Arc<AtomicUsize>,
618 ready: Arc<EndpointEventReady>,
619 closed: bool,
620}
621
622#[derive(Debug, Default)]
623struct EndpointEventReady {
624 sequence: StdMutex<u64>,
625 changed: Condvar,
626}
627
628impl EndpointEventReady {
629 fn notify(&self) {
630 if let Ok(mut sequence) = self.sequence.lock() {
631 *sequence = sequence.wrapping_add(1);
632 self.changed.notify_one();
633 }
634 }
635
636 fn snapshot(&self) -> u64 {
637 self.sequence.lock().map(|sequence| *sequence).unwrap_or(0)
638 }
639
640 fn wait_for_change(&self, observed: &mut u64) {
641 let Ok(mut sequence) = self.sequence.lock() else {
642 return;
643 };
644 while *sequence == *observed {
645 match self.changed.wait(sequence) {
646 Ok(next) => sequence = next,
647 Err(_) => return,
648 }
649 }
650 *observed = *sequence;
651 }
652}
653
654fn endpoint_event_capacity(requested: usize) -> usize {
655 requested.max(1)
656}
657
658fn try_reserve_endpoint_event_messages(
659 counter: &AtomicUsize,
660 capacity: usize,
661 count: usize,
662) -> Option<usize> {
663 if count == 0 {
664 return Some(counter.load(Relaxed));
665 }
666
667 counter
668 .fetch_update(Relaxed, Relaxed, |current| {
669 current.checked_add(count).filter(|next| *next <= capacity)
670 })
671 .ok()
672}
673
674#[derive(Debug, Default)]
681pub(in crate::node) struct EndpointEventRuntime {
682 sender: Option<EndpointEventSender>,
683}
684
685impl EndpointEventSender {
686 pub(in crate::node) fn channel(capacity: usize) -> (Self, EndpointEventReceiver) {
687 Self::channel_with_direct_sink(capacity, None)
688 }
689
690 pub(in crate::node) fn channel_with_direct_sink(
691 capacity: usize,
692 direct_sink: Option<EndpointDirectSink>,
693 ) -> (Self, EndpointEventReceiver) {
694 let message_cap = endpoint_event_capacity(capacity);
695 let (tx, rx) = tokio::sync::mpsc::channel(message_cap);
696 let queued_messages = Arc::new(AtomicUsize::new(0));
697 let ready = Arc::new(EndpointEventReady::default());
698 (
699 Self {
700 tx,
701 direct_sink,
702 queued_messages: Arc::clone(&queued_messages),
703 ready: Arc::clone(&ready),
704 message_cap,
705 },
706 EndpointEventReceiver {
707 rx,
708 queued_messages,
709 ready,
710 closed: false,
711 },
712 )
713 }
714
715 pub(crate) fn direct_sink(&self) -> Option<&EndpointDirectSink> {
716 self.direct_sink.as_ref()
717 }
718
719 pub(crate) fn send(&self, event: NodeEndpointEvent) -> Result<(), EndpointEventSendError> {
720 if event.messages.is_empty() {
721 return Ok(());
722 }
723
724 self.send_event(event, true)
725 }
726
727 fn send_event(
728 &self,
729 event: NodeEndpointEvent,
730 split_on_pressure: bool,
731 ) -> Result<(), EndpointEventSendError> {
732 let count = event.message_count();
733 let Some(previous) =
734 try_reserve_endpoint_event_messages(&self.queued_messages, self.message_cap, count)
735 else {
736 if split_on_pressure && count > 1 {
737 return self.split_and_send_event(event);
738 }
739 crate::perf_profile::record_event_count(
740 crate::perf_profile::Event::EndpointEventBulkDropped,
741 count as u64,
742 );
743 return Ok(());
744 };
745
746 let queued = previous.saturating_add(count);
747 match self.tx.try_send(event) {
748 Ok(()) => {
749 self.note_send_success(previous, queued);
750 Ok(())
751 }
752 Err(tokio::sync::mpsc::error::TrySendError::Full(_event)) => {
753 self.note_send_rejected(count);
754 crate::perf_profile::record_event_count(
755 crate::perf_profile::Event::EndpointEventBulkDropped,
756 count as u64,
757 );
758 Ok(())
759 }
760 Err(tokio::sync::mpsc::error::TrySendError::Closed(event)) => {
761 self.note_send_rejected(count);
762 drop(event);
763 Err(EndpointEventSendError::Closed)
764 }
765 }
766 }
767
768 fn split_and_send_event(&self, event: NodeEndpointEvent) -> Result<(), EndpointEventSendError> {
769 let mut messages = event.messages;
770 let queued_at = event.queued_at;
771 if messages.len() <= 1 {
772 return self.send_event(
773 NodeEndpointEvent {
774 messages,
775 queued_at,
776 },
777 false,
778 );
779 }
780
781 let right = messages.split_off(messages.len() / 2);
782 if !messages.is_empty() {
783 self.send_event(
784 NodeEndpointEvent {
785 messages,
786 queued_at,
787 },
788 true,
789 )?;
790 }
791 if !right.is_empty() {
792 self.send_event(
793 NodeEndpointEvent {
794 messages: right,
795 queued_at,
796 },
797 true,
798 )?;
799 }
800 Ok(())
801 }
802
803 fn note_send_success(&self, previous: usize, queued: usize) {
804 if previous < ENDPOINT_EVENT_BACKLOG_HIGH_WATER
805 && queued >= ENDPOINT_EVENT_BACKLOG_HIGH_WATER
806 {
807 crate::perf_profile::record_event(crate::perf_profile::Event::EndpointEventBacklogHigh);
808 }
809 self.ready.notify();
810 }
811
812 fn note_send_rejected(&self, count: usize) {
813 release_endpoint_event_messages(&self.queued_messages, count);
814 self.ready.notify();
815 }
816
817 #[cfg(test)]
818 pub(crate) fn queued_messages(&self) -> usize {
819 self.queued_messages.load(Relaxed)
820 }
821}
822
823impl Drop for EndpointEventSender {
824 fn drop(&mut self) {
825 self.ready.notify();
826 }
827}
828
829impl Drop for EndpointEventReceiver {
830 fn drop(&mut self) {
831 self.queued_messages.store(0, Relaxed);
832 self.ready.notify();
833 }
834}
835
836impl EndpointEventRuntime {
837 pub(in crate::node) fn attach(&mut self, sender: EndpointEventSender) {
838 self.sender = Some(sender);
839 }
840
841 pub(in crate::node) fn is_attached(&self) -> bool {
842 self.sender.is_some()
843 }
844
845 pub(in crate::node) fn sender(&self) -> Option<EndpointEventSender> {
846 self.sender.clone()
847 }
848
849 pub(in crate::node) fn deliver_endpoint_data_batch(
850 &mut self,
851 messages: Vec<EndpointDataDelivery>,
852 ) -> Result<(), EndpointEventSendError> {
853 if messages.is_empty() {
854 return Ok(());
855 }
856
857 let Some(sender) = &self.sender else {
858 return Ok(());
859 };
860 let _t_deliver =
861 crate::perf_profile::Timer::start(crate::perf_profile::Stage::EndpointDeliver);
862 sender.send(NodeEndpointEvent {
863 messages,
864 queued_at: crate::perf_profile::stamp(),
865 })
866 }
867}
868
869impl EndpointEventReceiver {
870 pub(crate) async fn recv(&mut self) -> Option<NodeEndpointEvent> {
871 let event = self.rx.recv().await?;
872 self.note_observed(&event);
873 Some(event)
874 }
875
876 pub(crate) fn blocking_recv(&mut self) -> Option<NodeEndpointEvent> {
877 let mut observed = self.ready.snapshot();
878 loop {
879 match self.try_recv() {
880 Ok(event) => return Some(event),
881 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return None,
882 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
883 self.ready.wait_for_change(&mut observed);
884 }
885 }
886 }
887 }
888
889 pub(crate) fn try_recv(
890 &mut self,
891 ) -> Result<NodeEndpointEvent, tokio::sync::mpsc::error::TryRecvError> {
892 match self.rx.try_recv() {
893 Ok(event) => {
894 self.note_observed(&event);
895 Ok(event)
896 }
897 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
898 if self.closed {
899 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
900 } else {
901 Err(tokio::sync::mpsc::error::TryRecvError::Empty)
902 }
903 }
904 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
905 self.closed = true;
906 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
907 }
908 }
909 }
910
911 pub(crate) fn release_messages(&self, count: usize) {
912 release_endpoint_event_messages(&self.queued_messages, count);
913 }
914
915 fn note_observed(&self, event: &NodeEndpointEvent) {
916 event.record_dequeue_wait();
917 }
918}
919
920pub(in crate::node) fn release_endpoint_event_messages(counter: &AtomicUsize, count: usize) {
921 if count == 0 {
922 return;
923 }
924
925 let previous = counter.fetch_sub(count, Relaxed);
926 debug_assert!(
927 previous >= count,
928 "endpoint event queued message accounting underflow"
929 );
930}
931
932#[derive(Debug, Clone, Default, PartialEq, Eq)]
934pub(crate) struct UpdatePeersOutcome {
935 pub(crate) added: usize,
936 pub(crate) removed: usize,
937 pub(crate) updated: usize,
938 pub(crate) unchanged: usize,
939}
940
941#[derive(Debug, Clone)]
947pub(crate) struct EndpointDataDelivery {
948 pub(crate) source_peer: PeerIdentity,
949 pub(crate) payload: PacketBuffer,
950 pub(crate) enqueued_at_ms: u64,
951}
952
953impl EndpointDataDelivery {
954 pub(crate) fn new(source_peer: PeerIdentity, payload: PacketBuffer) -> Self {
955 Self {
956 source_peer,
957 payload,
958 enqueued_at_ms: crate::time::now_ms(),
959 }
960 }
961}
962
963#[derive(Debug)]
965pub(crate) struct NodeEndpointEvent {
966 pub(crate) messages: Vec<EndpointDataDelivery>,
967 pub(crate) queued_at: Option<crate::perf_profile::TraceStamp>,
968}
969
970impl NodeEndpointEvent {
971 pub(in crate::node) fn message_count(&self) -> usize {
972 self.messages.len()
973 }
974
975 fn record_dequeue_wait(&self) {
976 let queued_at = self.queued_at;
977 if queued_at.is_none() {
978 return;
979 }
980 crate::perf_profile::record_since_count(
981 crate::perf_profile::Stage::EndpointEventWait,
982 queued_at,
983 self.message_count() as u64,
984 );
985 }
986}
987
988#[derive(Debug, Clone, PartialEq, Eq)]
990pub(crate) struct NodeEndpointPeer {
991 pub(crate) npub: String,
992 pub(crate) node_addr: NodeAddr,
993 pub(crate) connected: bool,
994 pub(crate) transport_addr: Option<String>,
995 pub(crate) transport_type: Option<String>,
996 pub(crate) link_id: u64,
997 pub(crate) srtt_ms: Option<u64>,
998 pub(crate) srtt_age_ms: Option<u64>,
999 pub(crate) packets_sent: u64,
1000 pub(crate) packets_recv: u64,
1001 pub(crate) bytes_sent: u64,
1002 pub(crate) bytes_recv: u64,
1003 pub(crate) rekey_in_progress: bool,
1004 pub(crate) rekey_draining: bool,
1005 pub(crate) current_k_bit: Option<bool>,
1006 pub(crate) last_outbound_route: Option<String>,
1007 pub(crate) direct_probe_pending: bool,
1008 pub(crate) direct_probe_after_ms: Option<u64>,
1009 pub(crate) direct_probe_retry_count: u32,
1010 pub(crate) direct_probe_auto_reconnect: bool,
1011 pub(crate) direct_probe_expires_at_ms: Option<u64>,
1012 pub(crate) nostr_traversal_consecutive_failures: u32,
1013 pub(crate) nostr_traversal_in_cooldown: bool,
1014 pub(crate) nostr_traversal_cooldown_until_ms: Option<u64>,
1015 pub(crate) nostr_traversal_last_observed_skew_ms: Option<i64>,
1016}
1017
1018#[derive(Debug, Clone, PartialEq, Eq)]
1020pub(crate) struct NodeEndpointRelayStatus {
1021 pub(crate) url: String,
1022 pub(crate) status: String,
1023}