1use super::*;
2use crate::transport::PacketBuffer;
3use std::collections::VecDeque;
4use std::sync::{Arc, Condvar, Mutex};
5use std::time::Duration;
6
7pub const FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS: usize = 128;
9
10pub const FIPS_ENDPOINT_DIRECT_PACKET_QUEUE_MAX_PACKETS: usize = 4096;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub(crate) struct FipsEndpointDirectPacketRunMeta {
16 source_peer: PeerIdentity,
17 previous_hop_addr: NodeAddr,
18 received_k_bit: bool,
19 direct_path: bool,
20 enqueued_at_ms: u64,
21}
22
23impl FipsEndpointDirectPacketRunMeta {
24 pub(crate) fn new(
25 source_peer: PeerIdentity,
26 previous_hop_addr: NodeAddr,
27 received_k_bit: bool,
28 direct_path: bool,
29 enqueued_at_ms: u64,
30 ) -> Self {
31 Self {
32 source_peer,
33 previous_hop_addr,
34 received_k_bit,
35 direct_path,
36 enqueued_at_ms,
37 }
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct FipsEndpointDirectPacketRun {
47 meta: FipsEndpointDirectPacketRunMeta,
48 packets: Vec<PacketBuffer>,
49 packet_bytes: usize,
50}
51
52pub struct FipsEndpointDirectPacketSlices<'a> {
54 packets: std::slice::Iter<'a, PacketBuffer>,
55}
56
57impl FipsEndpointDirectPacketRun {
58 pub(crate) fn from_packet(meta: FipsEndpointDirectPacketRunMeta, packet: PacketBuffer) -> Self {
59 let packet_bytes = packet.len();
60 let mut packets = Vec::with_capacity(FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS);
61 packets.push(packet);
62 Self {
63 meta,
64 packets,
65 packet_bytes,
66 }
67 }
68
69 pub fn source_peer(&self) -> &PeerIdentity {
71 &self.meta.source_peer
72 }
73
74 pub fn enqueued_at_ms(&self) -> u64 {
76 self.meta.enqueued_at_ms
77 }
78
79 pub fn len(&self) -> usize {
81 self.packets.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.len() == 0
87 }
88
89 pub fn packet_bytes(&self) -> usize {
91 self.packet_bytes
92 }
93
94 pub fn packet_slice(&self, index: usize) -> Option<&[u8]> {
96 self.packets.get(index).map(PacketBuffer::as_slice)
97 }
98
99 pub(crate) fn push_packet(&mut self, packet: PacketBuffer) {
100 self.packet_bytes = self.packet_bytes.saturating_add(packet.len());
101 self.packets.push(packet);
102 }
103
104 pub(crate) fn try_append(&mut self, other: &mut Self) -> bool {
105 if self.len().saturating_add(other.len()) > FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS
106 || self.meta.source_peer != other.meta.source_peer
107 || self.meta.previous_hop_addr != other.meta.previous_hop_addr
108 || self.meta.received_k_bit != other.meta.received_k_bit
109 || self.meta.direct_path != other.meta.direct_path
110 {
111 return false;
112 }
113 self.packet_bytes = self.packet_bytes.saturating_add(other.packet_bytes);
114 other.packet_bytes = 0;
115 self.packets.append(&mut other.packets);
116 true
117 }
118
119 pub fn packet_slices(&self) -> FipsEndpointDirectPacketSlices<'_> {
121 FipsEndpointDirectPacketSlices {
122 packets: self.packets.iter(),
123 }
124 }
125
126 pub fn retain_packets<F>(&mut self, mut keep: F)
132 where
133 F: FnMut(usize, &[u8]) -> bool,
134 {
135 let mut index = 0usize;
136 let mut packet_bytes = 0usize;
137 self.packets.retain(|packet| {
138 let retained = keep(index, packet.as_slice());
139 index = index.saturating_add(1);
140 if retained {
141 packet_bytes = packet_bytes.saturating_add(packet.len());
142 }
143 retained
144 });
145 self.packet_bytes = packet_bytes;
146 }
147
148 pub fn split_off_packets(&mut self, at: usize) -> Option<Self> {
153 if at >= self.packets.len() {
154 return None;
155 }
156 let packets = self.packets.split_off(at);
157 let packet_bytes = packets.iter().map(PacketBuffer::len).sum();
158 self.packet_bytes = self.packet_bytes.saturating_sub(packet_bytes);
159 Some(Self {
160 meta: self.meta.clone(),
161 packets,
162 packet_bytes,
163 })
164 }
165}
166
167impl<'a> Iterator for FipsEndpointDirectPacketSlices<'a> {
168 type Item = &'a [u8];
169
170 fn next(&mut self) -> Option<Self::Item> {
171 self.packets.next().map(PacketBuffer::as_slice)
172 }
173
174 fn size_hint(&self) -> (usize, Option<usize>) {
175 self.packets.size_hint()
176 }
177}
178
179impl ExactSizeIterator for FipsEndpointDirectPacketSlices<'_> {}
180
181impl Drop for FipsEndpointDirectPacketRun {
182 fn drop(&mut self) {
183 PacketBuffer::recycle_batch(&mut self.packets);
184 }
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct FipsEndpointDirectPacketBatch {
190 packet_runs: Vec<FipsEndpointDirectPacketRun>,
191}
192
193impl FipsEndpointDirectPacketBatch {
194 pub(crate) fn from_packet_runs(packet_runs: Vec<FipsEndpointDirectPacketRun>) -> Self {
195 debug_assert!(
196 packet_runs
197 .iter()
198 .all(|run| run.len() <= FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS)
199 );
200 Self { packet_runs }
201 }
202
203 pub fn into_packet_runs(self) -> Vec<FipsEndpointDirectPacketRun> {
205 self.packet_runs
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
211pub enum FipsEndpointDirectDeliveryError {
212 #[error("direct endpoint sink unavailable")]
214 Unavailable,
215}
216
217pub trait FipsEndpointDirectSink: Send + Sync + 'static {
222 fn deliver_endpoint_packet_batch(
224 &self,
225 batch: FipsEndpointDirectPacketBatch,
226 ) -> Result<(), FipsEndpointDirectDeliveryError>;
227}
228
229impl<F> FipsEndpointDirectSink for F
230where
231 F: Fn(FipsEndpointDirectPacketBatch) -> Result<(), FipsEndpointDirectDeliveryError>
232 + Send
233 + Sync
234 + 'static,
235{
236 fn deliver_endpoint_packet_batch(
237 &self,
238 batch: FipsEndpointDirectPacketBatch,
239 ) -> Result<(), FipsEndpointDirectDeliveryError> {
240 self(batch)
241 }
242}
243
244#[derive(Clone)]
245pub(crate) struct EndpointDirectSink {
246 sink: Arc<dyn FipsEndpointDirectSink>,
247}
248
249impl std::fmt::Debug for EndpointDirectSink {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 f.debug_struct("EndpointDirectSink").finish_non_exhaustive()
252 }
253}
254
255impl EndpointDirectSink {
256 pub(crate) fn new<S>(sink: S) -> Self
257 where
258 S: FipsEndpointDirectSink,
259 {
260 Self {
261 sink: Arc::new(sink),
262 }
263 }
264
265 pub(crate) fn deliver_direct_packet_batch(
266 &self,
267 batch: FipsEndpointDirectPacketBatch,
268 ) -> Result<(), FipsEndpointDirectDeliveryError> {
269 self.sink.deliver_endpoint_packet_batch(batch)
270 }
271}
272
273struct FipsEndpointDirectReceiverSink {
274 shared: Arc<FipsEndpointDirectReceiverShared>,
275}
276
277#[derive(Debug)]
279pub struct FipsEndpointDirectReceiver {
280 shared: Arc<FipsEndpointDirectReceiverShared>,
281}
282
283#[derive(Debug)]
284struct FipsEndpointDirectReceiverShared {
285 state: Mutex<FipsEndpointDirectReceiverState>,
286 ready: Condvar,
287}
288
289#[derive(Debug, Default)]
290struct FipsEndpointDirectReceiverState {
291 runs: VecDeque<FipsEndpointDirectPacketRun>,
292 packets: usize,
293 interrupted: bool,
294}
295
296impl FipsEndpointDirectReceiver {
297 pub(crate) fn channel() -> (impl FipsEndpointDirectSink, Self) {
298 let shared = Arc::new(FipsEndpointDirectReceiverShared {
299 state: Mutex::new(FipsEndpointDirectReceiverState::default()),
300 ready: Condvar::new(),
301 });
302 (
303 FipsEndpointDirectReceiverSink {
304 shared: Arc::clone(&shared),
305 },
306 Self { shared },
307 )
308 }
309
310 pub fn recv_timeout(
312 &self,
313 timeout: Duration,
314 packet_limit: usize,
315 ) -> Result<Vec<FipsEndpointDirectPacketRun>, std::sync::mpsc::RecvTimeoutError> {
316 let mut state = self
317 .shared
318 .state
319 .lock()
320 .map_err(|_| std::sync::mpsc::RecvTimeoutError::Disconnected)?;
321 if state.runs.is_empty() {
322 let (next, wait) = self
323 .shared
324 .ready
325 .wait_timeout_while(state, timeout, |state| {
326 state.runs.is_empty() && !state.interrupted
327 })
328 .map_err(|_| std::sync::mpsc::RecvTimeoutError::Disconnected)?;
329 state = next;
330 let interrupted = std::mem::take(&mut state.interrupted);
331 if state.runs.is_empty() && (interrupted || wait.timed_out()) {
332 return Err(std::sync::mpsc::RecvTimeoutError::Timeout);
333 }
334 }
335 take_direct_source_runs(&mut state, packet_limit)
336 .ok_or(std::sync::mpsc::RecvTimeoutError::Timeout)
337 }
338
339 pub fn try_recv(
341 &self,
342 packet_limit: usize,
343 ) -> Result<Vec<FipsEndpointDirectPacketRun>, std::sync::mpsc::TryRecvError> {
344 let mut state = self
345 .shared
346 .state
347 .lock()
348 .map_err(|_| std::sync::mpsc::TryRecvError::Disconnected)?;
349 let limit = direct_receiver_packet_limit(packet_limit);
350 if state.runs.front().is_some_and(|run| run.len() > limit) {
351 return Err(std::sync::mpsc::TryRecvError::Empty);
352 }
353 take_direct_source_runs(&mut state, limit).ok_or(std::sync::mpsc::TryRecvError::Empty)
354 }
355
356 pub fn interrupt(&self) {
358 if let Ok(mut state) = self.shared.state.lock() {
359 state.interrupted = true;
360 }
361 self.shared.ready.notify_all();
362 }
363}
364
365impl FipsEndpointDirectSink for FipsEndpointDirectReceiverSink {
366 fn deliver_endpoint_packet_batch(
367 &self,
368 batch: FipsEndpointDirectPacketBatch,
369 ) -> Result<(), FipsEndpointDirectDeliveryError> {
370 let runs = batch.into_packet_runs();
371 if runs.is_empty() {
372 return Ok(());
373 }
374 let packets = runs
375 .iter()
376 .map(FipsEndpointDirectPacketRun::len)
377 .sum::<usize>();
378 let mut state = self
379 .shared
380 .state
381 .lock()
382 .map_err(|_| FipsEndpointDirectDeliveryError::Unavailable)?;
383 let queued = state
384 .packets
385 .checked_add(packets)
386 .filter(|queued| *queued <= FIPS_ENDPOINT_DIRECT_PACKET_QUEUE_MAX_PACKETS)
387 .ok_or(FipsEndpointDirectDeliveryError::Unavailable)?;
388 let wake = state.runs.is_empty();
389 state.packets = queued;
390 state.runs.extend(runs);
391 drop(state);
392 if wake {
393 self.shared.ready.notify_one();
394 }
395 Ok(())
396 }
397}
398
399fn direct_receiver_packet_limit(limit: usize) -> usize {
400 limit
401 .max(1)
402 .min(FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS)
403}
404
405fn take_direct_source_runs(
406 state: &mut FipsEndpointDirectReceiverState,
407 packet_limit: usize,
408) -> Option<Vec<FipsEndpointDirectPacketRun>> {
409 let first = state.runs.pop_front()?;
410 let source = *first.source_peer().node_addr();
411 let mut packets = first.len();
412 let mut runs = vec![first];
413 let limit = direct_receiver_packet_limit(packet_limit);
414 while packets < limit {
415 let Some(next) = state.runs.front() else {
416 break;
417 };
418 let next_packets = next.len();
419 if next.source_peer().node_addr() != &source || packets.saturating_add(next_packets) > limit
420 {
421 break;
422 }
423 packets = packets.saturating_add(next_packets);
424 runs.push(state.runs.pop_front().expect("front direct run must exist"));
425 }
426 state.packets = state
427 .packets
428 .checked_sub(packets)
429 .expect("direct receiver packet backlog must cover removed runs");
430 Some(runs)
431}
432
433#[derive(Debug)]
435pub struct ExternalPacketIo {
436 pub outbound_tx: crate::upper::tun::TunOutboundTx,
438 pub inbound_rx: tokio::sync::mpsc::Receiver<NodeDeliveredPacket>,
440}
441
442#[derive(Debug)]
444pub(crate) struct EndpointDataIo {
445 pub(crate) control_tx: tokio::sync::mpsc::Sender<NodeEndpointControlCommand>,
448 pub(crate) data_batch_tx: EndpointDataBatchTx,
454 pub(crate) event_rx: EndpointEventReceiver,
462 pub(crate) event_tx: EndpointEventSender,
466 pub(crate) service_event_rx: EndpointServiceEventReceiver,
468 pub(crate) service_event_tx: EndpointServiceEventSender,
470}
471
472#[derive(Debug, Clone)]
474pub(crate) struct EndpointEventSender {
475 tx: tokio::sync::mpsc::Sender<NodeEndpointEvent>,
476 direct_sink: Option<EndpointDirectSink>,
477 queued_messages: Arc<AtomicUsize>,
478 ready: Arc<EndpointEventReady>,
479 message_cap: usize,
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
483pub(crate) enum EndpointEventSendError {
484 #[error("endpoint event channel closed")]
485 Closed,
486}
487
488#[derive(Debug)]
489pub(crate) struct EndpointEventReceiver {
490 rx: tokio::sync::mpsc::Receiver<NodeEndpointEvent>,
491 queued_messages: Arc<AtomicUsize>,
492 ready: Arc<EndpointEventReady>,
493 closed: bool,
494}
495
496#[derive(Debug, Default)]
497struct EndpointEventReady {
498 sequence: StdMutex<u64>,
499 changed: Condvar,
500}
501
502impl EndpointEventReady {
503 fn notify(&self) {
504 if let Ok(mut sequence) = self.sequence.lock() {
505 *sequence = sequence.wrapping_add(1);
506 self.changed.notify_one();
507 }
508 }
509
510 fn snapshot(&self) -> u64 {
511 self.sequence.lock().map(|sequence| *sequence).unwrap_or(0)
512 }
513
514 fn wait_for_change(&self, observed: &mut u64) {
515 let Ok(mut sequence) = self.sequence.lock() else {
516 return;
517 };
518 while *sequence == *observed {
519 match self.changed.wait(sequence) {
520 Ok(next) => sequence = next,
521 Err(_) => return,
522 }
523 }
524 *observed = *sequence;
525 }
526}
527
528fn endpoint_event_capacity(requested: usize) -> usize {
529 requested.max(1)
530}
531
532fn try_reserve_endpoint_event_messages(
533 counter: &AtomicUsize,
534 capacity: usize,
535 count: usize,
536) -> Option<usize> {
537 if count == 0 {
538 return Some(counter.load(Relaxed));
539 }
540
541 counter
542 .fetch_update(Relaxed, Relaxed, |current| {
543 current.checked_add(count).filter(|next| *next <= capacity)
544 })
545 .ok()
546}
547
548#[derive(Debug, Default)]
555pub(in crate::node) struct EndpointEventRuntime {
556 sender: Option<EndpointEventSender>,
557}
558
559impl EndpointEventSender {
560 pub(in crate::node) fn channel(capacity: usize) -> (Self, EndpointEventReceiver) {
561 Self::channel_with_direct_sink(capacity, None)
562 }
563
564 pub(in crate::node) fn channel_with_direct_sink(
565 capacity: usize,
566 direct_sink: Option<EndpointDirectSink>,
567 ) -> (Self, EndpointEventReceiver) {
568 let message_cap = endpoint_event_capacity(capacity);
569 let (tx, rx) = tokio::sync::mpsc::channel(message_cap);
570 let queued_messages = Arc::new(AtomicUsize::new(0));
571 let ready = Arc::new(EndpointEventReady::default());
572 (
573 Self {
574 tx,
575 direct_sink,
576 queued_messages: Arc::clone(&queued_messages),
577 ready: Arc::clone(&ready),
578 message_cap,
579 },
580 EndpointEventReceiver {
581 rx,
582 queued_messages,
583 ready,
584 closed: false,
585 },
586 )
587 }
588
589 pub(crate) fn direct_sink(&self) -> Option<&EndpointDirectSink> {
590 self.direct_sink.as_ref()
591 }
592
593 pub(crate) fn send(&self, event: NodeEndpointEvent) -> Result<(), EndpointEventSendError> {
594 if event.messages.is_empty() {
595 return Ok(());
596 }
597
598 self.send_event(event, true)
599 }
600
601 fn send_event(
602 &self,
603 event: NodeEndpointEvent,
604 split_on_pressure: bool,
605 ) -> Result<(), EndpointEventSendError> {
606 let count = event.message_count();
607 let Some(previous) =
608 try_reserve_endpoint_event_messages(&self.queued_messages, self.message_cap, count)
609 else {
610 if split_on_pressure && count > 1 {
611 return self.split_and_send_event(event);
612 }
613 crate::perf_profile::record_event_count(
614 crate::perf_profile::Event::EndpointEventBulkDropped,
615 count as u64,
616 );
617 return Ok(());
618 };
619
620 let queued = previous.saturating_add(count);
621 match self.tx.try_send(event) {
622 Ok(()) => {
623 self.note_send_success(previous, queued);
624 Ok(())
625 }
626 Err(tokio::sync::mpsc::error::TrySendError::Full(_event)) => {
627 self.note_send_rejected(count);
628 crate::perf_profile::record_event_count(
629 crate::perf_profile::Event::EndpointEventBulkDropped,
630 count as u64,
631 );
632 Ok(())
633 }
634 Err(tokio::sync::mpsc::error::TrySendError::Closed(event)) => {
635 self.note_send_rejected(count);
636 drop(event);
637 Err(EndpointEventSendError::Closed)
638 }
639 }
640 }
641
642 fn split_and_send_event(&self, event: NodeEndpointEvent) -> Result<(), EndpointEventSendError> {
643 let mut messages = event.messages;
644 let queued_at = event.queued_at;
645 if messages.len() <= 1 {
646 return self.send_event(
647 NodeEndpointEvent {
648 messages,
649 queued_at,
650 },
651 false,
652 );
653 }
654
655 let right = messages.split_off(messages.len() / 2);
656 if !messages.is_empty() {
657 self.send_event(
658 NodeEndpointEvent {
659 messages,
660 queued_at,
661 },
662 true,
663 )?;
664 }
665 if !right.is_empty() {
666 self.send_event(
667 NodeEndpointEvent {
668 messages: right,
669 queued_at,
670 },
671 true,
672 )?;
673 }
674 Ok(())
675 }
676
677 fn note_send_success(&self, previous: usize, queued: usize) {
678 if previous < ENDPOINT_EVENT_BACKLOG_HIGH_WATER
679 && queued >= ENDPOINT_EVENT_BACKLOG_HIGH_WATER
680 {
681 crate::perf_profile::record_event(crate::perf_profile::Event::EndpointEventBacklogHigh);
682 }
683 self.ready.notify();
684 }
685
686 fn note_send_rejected(&self, count: usize) {
687 release_endpoint_event_messages(&self.queued_messages, count);
688 self.ready.notify();
689 }
690
691 #[cfg(test)]
692 pub(crate) fn queued_messages(&self) -> usize {
693 self.queued_messages.load(Relaxed)
694 }
695}
696
697impl Drop for EndpointEventSender {
698 fn drop(&mut self) {
699 self.ready.notify();
700 }
701}
702
703impl Drop for EndpointEventReceiver {
704 fn drop(&mut self) {
705 self.queued_messages.store(0, Relaxed);
706 self.ready.notify();
707 }
708}
709
710impl EndpointEventRuntime {
711 pub(in crate::node) fn attach(&mut self, sender: EndpointEventSender) {
712 self.sender = Some(sender);
713 }
714
715 pub(in crate::node) fn is_attached(&self) -> bool {
716 self.sender.is_some()
717 }
718
719 pub(in crate::node) fn sender(&self) -> Option<EndpointEventSender> {
720 self.sender.clone()
721 }
722
723 pub(in crate::node) fn deliver_endpoint_data_batch(
724 &mut self,
725 messages: Vec<EndpointDataDelivery>,
726 ) -> Result<(), EndpointEventSendError> {
727 if messages.is_empty() {
728 return Ok(());
729 }
730
731 let Some(sender) = &self.sender else {
732 return Ok(());
733 };
734 let _t_deliver =
735 crate::perf_profile::Timer::start(crate::perf_profile::Stage::EndpointDeliver);
736 sender.send(NodeEndpointEvent {
737 messages,
738 queued_at: crate::perf_profile::stamp(),
739 })
740 }
741}
742
743impl EndpointEventReceiver {
744 pub(crate) async fn recv(&mut self) -> Option<NodeEndpointEvent> {
745 let event = self.rx.recv().await?;
746 self.note_observed(&event);
747 Some(event)
748 }
749
750 pub(crate) fn blocking_recv(&mut self) -> Option<NodeEndpointEvent> {
751 let mut observed = self.ready.snapshot();
752 loop {
753 match self.try_recv() {
754 Ok(event) => return Some(event),
755 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return None,
756 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
757 self.ready.wait_for_change(&mut observed);
758 }
759 }
760 }
761 }
762
763 pub(crate) fn try_recv(
764 &mut self,
765 ) -> Result<NodeEndpointEvent, tokio::sync::mpsc::error::TryRecvError> {
766 match self.rx.try_recv() {
767 Ok(event) => {
768 self.note_observed(&event);
769 Ok(event)
770 }
771 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
772 if self.closed {
773 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
774 } else {
775 Err(tokio::sync::mpsc::error::TryRecvError::Empty)
776 }
777 }
778 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
779 self.closed = true;
780 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected)
781 }
782 }
783 }
784
785 pub(crate) fn release_messages(&self, count: usize) {
786 release_endpoint_event_messages(&self.queued_messages, count);
787 }
788
789 fn note_observed(&self, event: &NodeEndpointEvent) {
790 event.record_dequeue_wait();
791 }
792}
793
794pub(in crate::node) fn release_endpoint_event_messages(counter: &AtomicUsize, count: usize) {
795 if count == 0 {
796 return;
797 }
798
799 let previous = counter.fetch_sub(count, Relaxed);
800 debug_assert!(
801 previous >= count,
802 "endpoint event queued message accounting underflow"
803 );
804}
805
806#[derive(Debug, Clone, Default, PartialEq, Eq)]
808pub(crate) struct UpdatePeersOutcome {
809 pub(crate) added: usize,
810 pub(crate) removed: usize,
811 pub(crate) updated: usize,
812 pub(crate) unchanged: usize,
813}
814
815#[derive(Debug, Clone)]
821pub(crate) struct EndpointDataDelivery {
822 pub(crate) source_peer: PeerIdentity,
823 pub(crate) payload: PacketBuffer,
824 pub(crate) enqueued_at_ms: u64,
825}
826
827impl EndpointDataDelivery {
828 pub(crate) fn new(source_peer: PeerIdentity, payload: PacketBuffer) -> Self {
829 Self {
830 source_peer,
831 payload,
832 enqueued_at_ms: crate::time::now_ms(),
833 }
834 }
835}
836
837#[derive(Debug)]
839pub(crate) struct NodeEndpointEvent {
840 pub(crate) messages: Vec<EndpointDataDelivery>,
841 pub(crate) queued_at: Option<crate::perf_profile::TraceStamp>,
842}
843
844impl NodeEndpointEvent {
845 pub(in crate::node) fn message_count(&self) -> usize {
846 self.messages.len()
847 }
848
849 fn record_dequeue_wait(&self) {
850 let queued_at = self.queued_at;
851 if queued_at.is_none() {
852 return;
853 }
854 crate::perf_profile::record_since_count(
855 crate::perf_profile::Stage::EndpointEventWait,
856 queued_at,
857 self.message_count() as u64,
858 );
859 }
860}
861
862#[derive(Debug, Clone, PartialEq, Eq)]
864pub(crate) struct NodeEndpointPeer {
865 pub(crate) npub: String,
866 pub(crate) node_addr: NodeAddr,
867 pub(crate) connected: bool,
868 pub(crate) transport_addr: Option<String>,
869 pub(crate) transport_type: Option<String>,
870 pub(crate) link_id: u64,
871 pub(crate) srtt_ms: Option<u64>,
872 pub(crate) srtt_age_ms: Option<u64>,
873 pub(crate) packets_sent: u64,
874 pub(crate) packets_recv: u64,
875 pub(crate) bytes_sent: u64,
876 pub(crate) bytes_recv: u64,
877 pub(crate) rekey_in_progress: bool,
878 pub(crate) rekey_draining: bool,
879 pub(crate) current_k_bit: Option<bool>,
880 pub(crate) last_outbound_route: Option<String>,
881 pub(crate) direct_probe_pending: bool,
882 pub(crate) direct_probe_after_ms: Option<u64>,
883 pub(crate) direct_probe_retry_count: u32,
884 pub(crate) direct_probe_auto_reconnect: bool,
885 pub(crate) direct_probe_expires_at_ms: Option<u64>,
886 pub(crate) nostr_traversal_consecutive_failures: u32,
887 pub(crate) nostr_traversal_in_cooldown: bool,
888 pub(crate) nostr_traversal_cooldown_until_ms: Option<u64>,
889 pub(crate) nostr_traversal_last_observed_skew_ms: Option<i64>,
890}
891
892#[derive(Debug, Clone, PartialEq, Eq)]
894pub(crate) struct NodeEndpointRelayStatus {
895 pub(crate) url: String,
896 pub(crate) status: String,
897}