1#[cfg(feature = "iface-auto")]
4pub mod auto;
5#[cfg(feature = "iface-kiss")]
6pub mod ax25_kiss;
7#[cfg(feature = "iface-backbone")]
8pub mod backbone;
9#[cfg(any(feature = "iface-rnode", feature = "iface-weave"))]
10pub(crate) mod device_writer;
11#[cfg(feature = "iface-i2p")]
12pub mod i2p;
13#[cfg(feature = "iface-kiss")]
14pub mod kiss_iface;
15#[cfg(feature = "iface-local")]
16pub mod local;
17pub(crate) mod loop_writer;
18#[cfg(feature = "iface-pipe")]
19pub mod pipe;
20pub mod registry;
21#[cfg(feature = "iface-rnode")]
22pub mod rnode;
23#[cfg(feature = "iface-serial")]
24pub mod serial_iface;
25pub(crate) mod stream_read;
26#[cfg(feature = "iface-tcp")]
27pub mod tcp;
28#[cfg(feature = "iface-tcp")]
29pub mod tcp_server;
30pub(crate) mod transmit_buffer;
31#[cfg(feature = "iface-udp")]
32pub mod udp;
33#[cfg(all(feature = "iface-weave", target_os = "linux"))]
34pub mod weave;
35
36use std::any::Any;
37use std::collections::HashMap;
38use std::io;
39use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
40use std::sync::mpsc::TrySendError;
41use std::sync::{Arc, Condvar, Mutex, MutexGuard};
42use std::thread;
43use std::time::{Duration, Instant};
44
45use crate::event::EventSender;
46use crate::ifac::IfacState;
47use rns_core::transport::types::{InterfaceId, InterfaceInfo};
48
49pub trait SocketProtector: Send + Sync {
52 fn protect(&self, fd: std::os::unix::io::RawFd) -> io::Result<()>;
53}
54
55fn socket_protector_slot() -> &'static std::sync::RwLock<Option<Arc<dyn SocketProtector>>> {
56 static SLOT: std::sync::OnceLock<std::sync::RwLock<Option<Arc<dyn SocketProtector>>>> =
57 std::sync::OnceLock::new();
58 SLOT.get_or_init(|| std::sync::RwLock::new(None))
59}
60
61pub struct SocketProtectorGuard {
67 protector: Arc<dyn SocketProtector>,
68}
69
70pub fn install_socket_protector(
71 protector: Arc<dyn SocketProtector>,
72) -> io::Result<SocketProtectorGuard> {
73 let mut slot = socket_protector_slot()
74 .write()
75 .unwrap_or_else(|poisoned| poisoned.into_inner());
76 if slot.is_some() {
77 return Err(io::Error::new(
78 io::ErrorKind::AlreadyExists,
79 "an underlay socket protector is already installed",
80 ));
81 }
82 *slot = Some(Arc::clone(&protector));
83 Ok(SocketProtectorGuard { protector })
84}
85
86#[cfg(test)]
87mod socket_protector_tests {
88 use super::*;
89 use std::sync::atomic::{AtomicUsize, Ordering};
90
91 static TEST_LOCK: Mutex<()> = Mutex::new(());
92 struct CountingProtector(Arc<AtomicUsize>);
93 impl SocketProtector for CountingProtector {
94 fn protect(&self, _fd: std::os::unix::io::RawFd) -> io::Result<()> {
95 self.0.fetch_add(1, Ordering::Relaxed);
96 Ok(())
97 }
98 }
99
100 #[test]
101 fn underlay_helper_invokes_installed_protector_without_mark() {
102 use std::os::fd::AsRawFd;
103 let _lock = TEST_LOCK.lock().unwrap();
104 let calls = Arc::new(AtomicUsize::new(0));
105 let _guard =
106 install_socket_protector(Arc::new(CountingProtector(Arc::clone(&calls)))).unwrap();
107 let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
108 apply_underlay_mark(socket.as_raw_fd(), None).unwrap();
109 assert!(calls.load(Ordering::Relaxed) >= 1);
110 }
111}
112
113impl Drop for SocketProtectorGuard {
114 fn drop(&mut self) {
115 let mut slot = socket_protector_slot()
116 .write()
117 .unwrap_or_else(|poisoned| poisoned.into_inner());
118 if slot
119 .as_ref()
120 .is_some_and(|installed| Arc::ptr_eq(installed, &self.protector))
121 {
122 *slot = None;
123 }
124 }
125}
126
127fn protect_underlay_socket(fd: std::os::unix::io::RawFd) -> io::Result<()> {
128 let protector = socket_protector_slot()
129 .read()
130 .unwrap_or_else(|poisoned| poisoned.into_inner())
131 .clone();
132 if let Some(protector) = protector {
133 protector.protect(fd)?;
134 }
135 Ok(())
136}
137
138#[cfg(target_os = "linux")]
142pub fn bind_to_device(fd: std::os::unix::io::RawFd, device: &str) -> io::Result<()> {
143 let dev_bytes = device.as_bytes();
144 if dev_bytes.len() >= libc::IFNAMSIZ {
145 return Err(io::Error::new(
146 io::ErrorKind::InvalidInput,
147 format!("device name too long: {}", device),
148 ));
149 }
150 let ret = unsafe {
151 libc::setsockopt(
152 fd,
153 libc::SOL_SOCKET,
154 libc::SO_BINDTODEVICE,
155 dev_bytes.as_ptr() as *const libc::c_void,
156 dev_bytes.len() as libc::socklen_t,
157 )
158 };
159 if ret != 0 {
160 return Err(io::Error::last_os_error());
161 }
162 Ok(())
163}
164
165#[cfg(target_os = "linux")]
172pub fn apply_underlay_mark(fd: std::os::unix::io::RawFd, mark: Option<u32>) -> io::Result<()> {
173 protect_underlay_socket(fd)?;
174 let Some(mark) = mark else {
175 return Ok(());
176 };
177 let ret = unsafe {
178 libc::setsockopt(
179 fd,
180 libc::SOL_SOCKET,
181 libc::SO_MARK,
182 &mark as *const _ as *const libc::c_void,
183 std::mem::size_of::<u32>() as libc::socklen_t,
184 )
185 };
186 if ret != 0 {
187 return Err(io::Error::last_os_error());
188 }
189 Ok(())
190}
191
192#[cfg(not(target_os = "linux"))]
193pub fn apply_underlay_mark(fd: std::os::unix::io::RawFd, mark: Option<u32>) -> io::Result<()> {
194 protect_underlay_socket(fd)?;
195 if mark.is_some() {
196 return Err(io::Error::new(
197 io::ErrorKind::Unsupported,
198 "underlay socket marks are only supported on Linux",
199 ));
200 }
201 Ok(())
202}
203
204#[cfg(target_os = "linux")]
206pub fn connect_tcp_with_options(
207 addr: &std::net::SocketAddr,
208 device: Option<&str>,
209 underlay_mark: Option<u32>,
210 timeout: Duration,
211) -> io::Result<std::net::TcpStream> {
212 use std::os::unix::io::AsRawFd;
213
214 let socket = socket2::Socket::new(
215 socket2::Domain::for_address(*addr),
216 socket2::Type::STREAM,
217 Some(socket2::Protocol::TCP),
218 )?;
219 apply_underlay_mark(socket.as_raw_fd(), underlay_mark)?;
220 if let Some(device) = device {
221 bind_to_device(socket.as_raw_fd(), device)?;
222 }
223 socket.connect_timeout(&(*addr).into(), timeout)?;
224 Ok(socket.into())
225}
226
227pub fn validate_underlay_mark(mark: Option<u32>) -> io::Result<()> {
229 let Some(_) = mark else {
230 return Ok(());
231 };
232 let socket = socket2::Socket::new(
233 socket2::Domain::IPV4,
234 socket2::Type::DGRAM,
235 Some(socket2::Protocol::UDP),
236 )?;
237 use std::os::unix::io::AsRawFd;
238 apply_underlay_mark(socket.as_raw_fd(), mark)
239}
240
241pub trait Writer: Send {
245 fn send_frame(&mut self, data: &[u8]) -> io::Result<()>;
246
247 fn send_frame_confirmed(&mut self, data: &[u8]) -> io::Result<()> {
251 self.send_frame(data)
252 }
253
254 #[doc(hidden)]
256 fn enqueue_confirmed(
257 &mut self,
258 _data: &[u8],
259 _completion: &mut Option<crate::link_send::Completion>,
260 ) -> io::Result<()> {
261 Err(io::Error::new(
262 io::ErrorKind::Unsupported,
263 "confirmed sends require an async interface writer",
264 ))
265 }
266
267 fn send_frame_deferred(&mut self, data: &[u8]) -> io::Result<()> {
271 self.send_frame(data)
272 }
273
274 fn send_shared_deferred(
277 &mut self,
278 data: &rns_core::transport::types::PacketBytes,
279 ) -> io::Result<()> {
280 self.send_frame_deferred(data)
281 }
282
283 fn wake(&mut self) {}
285
286 fn flush_pending(&mut self) {}
290
291 fn send_frames(&mut self, frames: &[crate::interface::FrameBytes]) -> io::Result<()> {
292 for frame in frames {
293 self.send_frame(frame)?;
294 }
295 Ok(())
296 }
297
298 fn flush(&mut self) -> io::Result<()> {
300 Ok(())
301 }
302
303 fn egress_control(&self) -> Option<EgressControl> {
306 None
307 }
308
309 fn sends_inline(&self) -> bool {
314 false
315 }
316
317 fn loop_socket(&self) -> Option<std::os::fd::RawFd> {
321 None
322 }
323
324 #[doc(hidden)]
328 fn loop_egress(&mut self) -> Option<loop_writer::LoopEgress> {
329 None
330 }
331
332 fn loop_flush_after(&self) -> Option<Duration> {
337 None
338 }
339
340 #[doc(hidden)]
343 fn tick(&mut self) {}
344
345 fn shutdown(&mut self) {}
346}
347
348#[derive(Default, Clone, Copy)]
352pub struct InterfaceIdHasher(u64);
353
354impl std::hash::Hasher for InterfaceIdHasher {
355 fn write(&mut self, bytes: &[u8]) {
356 for byte in bytes {
357 self.write_u64(u64::from(*byte));
358 }
359 }
360
361 fn write_u64(&mut self, value: u64) {
362 self.0 = (self.0 ^ value).wrapping_mul(0x9E37_79B9_7F4A_7C15);
363 }
364
365 fn finish(&self) -> u64 {
366 self.0 ^ (self.0 >> 29)
367 }
368}
369
370pub type InterfaceMap<V> =
372 HashMap<InterfaceId, V, std::hash::BuildHasherDefault<InterfaceIdHasher>>;
373
374pub const DEFAULT_ASYNC_WRITER_QUEUE_CAPACITY: usize = 256;
375
376pub const ASYNC_WRITER_BACKPRESSURE_WAIT: Duration = Duration::from_millis(20);
385
386#[derive(Clone)]
387#[doc(hidden)]
388pub struct EgressControl {
389 byte_limit: usize,
390 reserved_bytes: Arc<AtomicUsize>,
391 stalled: Arc<AtomicBool>,
392 dropped_frames: Arc<AtomicU64>,
393 dropped_bytes: Arc<AtomicU64>,
394}
395
396impl EgressControl {
397 pub(crate) fn new(byte_limit: usize) -> Self {
398 Self {
399 byte_limit,
400 reserved_bytes: Arc::new(AtomicUsize::new(0)),
401 stalled: Arc::new(AtomicBool::new(false)),
402 dropped_frames: Arc::new(AtomicU64::new(0)),
403 dropped_bytes: Arc::new(AtomicU64::new(0)),
404 }
405 }
406
407 fn try_reserve(&self, bytes: usize) -> bool {
408 if self.stalled.load(Ordering::Relaxed) {
409 return false;
410 }
411 let mut current = self.reserved_bytes.load(Ordering::Relaxed);
412 loop {
413 let Some(next) = current.checked_add(bytes) else {
414 return false;
415 };
416 if next > self.byte_limit {
417 return false;
418 }
419 match self.reserved_bytes.compare_exchange_weak(
420 current,
421 next,
422 Ordering::Relaxed,
423 Ordering::Relaxed,
424 ) {
425 Ok(_) => return true,
426 Err(observed) => current = observed,
427 }
428 }
429 }
430
431 fn release(&self, bytes: usize) {
432 self.reserved_bytes.fetch_sub(bytes, Ordering::Relaxed);
433 }
434
435 pub(crate) fn stalled(&self) -> bool {
436 self.stalled.load(Ordering::Relaxed)
437 }
438
439 pub(crate) fn set_stalled(&self, stalled: bool) {
440 self.stalled.store(stalled, Ordering::Relaxed);
441 }
442
443 pub(crate) fn record_drop(&self, bytes: usize) {
444 self.dropped_frames.fetch_add(1, Ordering::Relaxed);
445 self.dropped_bytes
446 .fetch_add(bytes as u64, Ordering::Relaxed);
447 }
448}
449
450pub fn shared_medium_hint(interface_type: &str) -> bool {
456 matches!(
457 interface_type,
458 "AX25KISSInterface"
459 | "KISSInterface"
460 | "PipeInterface"
461 | "RNodeInterface"
462 | "RNodeMultiInterface"
463 | "SerialInterface"
464 | "UDPInterface"
465 )
466}
467
468pub(crate) const fn auto_mtu_for_bitrate(bitrate: u64) -> Option<u32> {
470 if bitrate >= 1_000_000_000 {
471 Some(524_288)
472 } else if bitrate >= 750_000_000 {
473 Some(262_144)
474 } else if bitrate >= 400_000_000 {
475 Some(131_072)
476 } else if bitrate >= 200_000_000 {
477 Some(65_536)
478 } else if bitrate >= 100_000_000 {
479 Some(32_768)
480 } else if bitrate >= 10_000_000 {
481 Some(16_384)
482 } else if bitrate >= 5_000_000 {
483 Some(8_192)
484 } else if bitrate >= 2_000_000 {
485 Some(4_096)
486 } else if bitrate >= 1_000_000 {
487 Some(2_048)
488 } else if bitrate >= 62_500 {
489 Some(1_024)
490 } else {
491 None
492 }
493}
494
495pub(crate) const TCP_DEFAULT_MTU: u32 = 65535;
497
498pub(crate) fn parse_tcp_rate_params(
502 params: &HashMap<String, String>,
503) -> Result<(Option<u64>, Option<u32>), String> {
504 let bitrate = match params.get("bitrate") {
505 Some(value) => Some(
506 value
507 .parse::<u64>()
508 .map_err(|_| format!("invalid bitrate {value:?}"))?,
509 ),
510 None => None,
511 };
512 let fixed_mtu = match params.get("fixed_mtu") {
513 Some(value) => {
514 let mtu = value
515 .parse::<u32>()
516 .map_err(|_| format!("invalid fixed_mtu {value:?}"))?;
517 if (mtu as usize) < rns_core::constants::MTU {
518 return Err(format!("Configured MTU of {mtu} bytes is too small"));
519 }
520 Some(mtu)
521 }
522 None => None,
523 };
524 Ok((bitrate, fixed_mtu))
525}
526
527pub(crate) const DEFAULT_FLUSH_AFTER: Option<Duration> = if cfg!(feature = "low-latency-flush") {
531 Some(Duration::from_micros(25))
532} else {
533 None
534};
535
536pub(crate) fn parse_flush_after(
541 params: &HashMap<String, String>,
542) -> Result<Option<Duration>, String> {
543 match params.get("flush_after_us") {
544 Some(value) if value.eq_ignore_ascii_case("off") => Ok(None),
545 Some(value) => value
546 .parse::<u64>()
547 .map(|us| Some(Duration::from_micros(us)))
548 .map_err(|_| format!("invalid flush_after_us {value:?}")),
549 None => Ok(DEFAULT_FLUSH_AFTER),
550 }
551}
552
553pub(crate) fn tcp_hardware_mtu(bitrate: Option<u64>, fixed_mtu: Option<u32>) -> u32 {
558 match (fixed_mtu, bitrate) {
559 (Some(mtu), _) => mtu,
560 (None, Some(bitrate)) => {
561 auto_mtu_for_bitrate(bitrate).unwrap_or(rns_core::constants::MTU as u32)
562 }
563 (None, None) => TCP_DEFAULT_MTU,
564 }
565}
566
567pub(crate) fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>, label: &str) -> MutexGuard<'a, T> {
568 match mutex.lock() {
569 Ok(guard) => guard,
570 Err(poisoned) => {
571 log::warn!("recovering poisoned mutex: {}", label);
572 poisoned.into_inner()
573 }
574 }
575}
576
577#[derive(Clone, Default)]
578pub struct ListenerControl {
579 stop: Arc<AtomicBool>,
580 running: Option<Arc<AtomicBool>>,
581 wake: Arc<std::sync::OnceLock<crate::listen::ListenerWake>>,
583}
584
585impl ListenerControl {
586 pub fn new() -> Self {
587 Self {
588 stop: Arc::new(AtomicBool::new(false)),
589 running: None,
590 wake: Arc::default(),
591 }
592 }
593
594 pub(crate) fn with_running_flag(running: Arc<AtomicBool>) -> Self {
595 Self {
596 stop: Arc::new(AtomicBool::new(false)),
597 running: Some(running),
598 wake: Arc::default(),
599 }
600 }
601
602 pub fn request_stop(&self) {
603 self.stop.store(true, Ordering::Relaxed);
604 if let Some(running) = &self.running {
605 running.store(false, Ordering::Relaxed);
606 }
607 if let Some(wake) = self.wake.get() {
608 wake.wake();
609 }
610 }
611
612 pub(crate) fn set_wake(&self, wake: crate::listen::ListenerWake) {
614 let _ = self.wake.set(wake);
615 }
618
619 pub fn should_stop(&self) -> bool {
620 self.stop.load(Ordering::Relaxed)
621 }
622
623 pub(crate) fn same_instance(&self, other: &Self) -> bool {
624 Arc::ptr_eq(&self.stop, &other.stop)
625 }
626}
627
628pub(crate) fn poll_readable(fd: std::os::fd::RawFd, timeout: Duration) -> io::Result<bool> {
630 let mut pfd = libc::pollfd {
631 fd,
632 events: libc::POLLIN,
633 revents: 0,
634 };
635 let timeout_ms = i32::try_from(timeout.as_millis()).unwrap_or(i32::MAX);
636 let result = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
637 if result < 0 {
638 let error = io::Error::last_os_error();
639 return if error.kind() == io::ErrorKind::Interrupted {
640 Ok(false)
641 } else {
642 Err(error)
643 };
644 }
645 if pfd.revents & libc::POLLNVAL != 0 {
646 return Err(io::Error::new(
647 io::ErrorKind::BrokenPipe,
648 "serial port closed",
649 ));
650 }
651 Ok(result > 0)
652}
653
654pub(crate) fn wait_while_active(control: &ListenerControl, duration: Duration) -> bool {
655 let deadline = Instant::now() + duration;
656 while !control.should_stop() {
657 let remaining = deadline.saturating_duration_since(Instant::now());
658 if remaining.is_zero() {
659 return true;
660 }
661 thread::sleep(remaining.min(Duration::from_millis(100)));
662 }
663 false
664}
665
666pub(crate) struct UnavailableWriter;
667
668impl Writer for UnavailableWriter {
669 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
670 Err(io::Error::new(
671 io::ErrorKind::NotConnected,
672 "interface is waiting for its port",
673 ))
674 }
675}
676
677#[derive(Clone, Default)]
678pub struct AsyncWriterMetrics {
679 queued_frames: Arc<AtomicUsize>,
680 worker_alive: Arc<AtomicBool>,
681 egress_control: Option<EgressControl>,
682 space: Arc<(Mutex<()>, Condvar)>,
684 space_waiters: Arc<AtomicUsize>,
688}
689
690impl AsyncWriterMetrics {
691 fn notify_space(&self) {
692 if self.space_waiters.load(Ordering::SeqCst) == 0 {
693 return;
694 }
695 let (lock, condvar) = &*self.space;
696 let _guard = lock_or_recover(lock, "async writer space");
697 condvar.notify_all();
698 }
699
700 pub fn queued_frames(&self) -> usize {
701 self.queued_frames.load(Ordering::Relaxed)
702 }
703
704 pub fn worker_alive(&self) -> bool {
705 self.worker_alive.load(Ordering::Relaxed)
706 }
707
708 pub fn tx_buffered(&self) -> usize {
710 self.egress_control
711 .as_ref()
712 .map(|control| control.reserved_bytes.load(Ordering::Relaxed))
713 .unwrap_or(0)
714 }
715
716 pub fn tx_drops(&self) -> u64 {
717 self.egress_control
718 .as_ref()
719 .map(|control| control.dropped_frames.load(Ordering::Relaxed))
720 .unwrap_or(0)
721 }
722
723 pub fn tx_dropped_bytes(&self) -> u64 {
724 self.egress_control
725 .as_ref()
726 .map(|control| control.dropped_bytes.load(Ordering::Relaxed))
727 .unwrap_or(0)
728 }
729
730 pub fn tx_stalled(&self) -> bool {
731 self.egress_control
732 .as_ref()
733 .is_some_and(EgressControl::stalled)
734 }
735}
736
737struct WriterQueue {
743 state: Mutex<WriterQueueState>,
744 ready: Condvar,
745 capacity: usize,
746}
747
748struct WriterQueueState {
749 frames: std::collections::VecDeque<QueuedFrame>,
750 sender_closed: bool,
751 receiver_closed: bool,
752 receiver_waiting: bool,
753}
754
755impl WriterQueue {
756 fn new(capacity: usize) -> Arc<Self> {
757 Arc::new(Self {
758 state: Mutex::new(WriterQueueState {
759 frames: std::collections::VecDeque::new(),
760 sender_closed: false,
761 receiver_closed: false,
762 receiver_waiting: false,
763 }),
764 ready: Condvar::new(),
765 capacity: capacity.max(1),
766 })
767 }
768
769 fn recv_all(&self, out: &mut Vec<QueuedFrame>) -> bool {
772 let mut state = lock_or_recover(&self.state, "async writer queue");
773 while state.frames.is_empty() {
774 if state.sender_closed {
775 return false;
776 }
777 state.receiver_waiting = true;
778 state = match self.ready.wait(state) {
782 Ok(state) => state,
783 Err(poisoned) => poisoned.into_inner(),
784 };
785 state.receiver_waiting = false;
786 }
787 out.extend(state.frames.drain(..));
788 true
789 }
790
791 fn kick(&self) {
793 let state = lock_or_recover(&self.state, "async writer queue");
794 let wake = state.receiver_waiting && !state.frames.is_empty();
795 drop(state);
796 if wake {
797 self.ready.notify_one();
798 }
799 }
800
801 fn close_receiver(&self) {
802 lock_or_recover(&self.state, "async writer queue").receiver_closed = true;
803 }
804}
805
806struct WriterTx(Arc<WriterQueue>);
808
809impl WriterTx {
810 fn try_send(
813 &self,
814 frame: QueuedFrame,
815 notify: bool,
816 ) -> Result<bool, TrySendError<QueuedFrame>> {
817 let mut state = lock_or_recover(&self.0.state, "async writer queue");
818 if state.receiver_closed {
819 return Err(TrySendError::Disconnected(frame));
820 }
821 if state.frames.len() >= self.0.capacity {
822 return Err(TrySendError::Full(frame));
823 }
824 state.frames.push_back(frame);
825 let waiting = state.receiver_waiting;
826 drop(state);
827 if waiting && notify {
828 self.0.ready.notify_one();
829 }
830 Ok(waiting && !notify)
831 }
832}
833
834impl Drop for WriterTx {
835 fn drop(&mut self) {
836 lock_or_recover(&self.0.state, "async writer queue").sender_closed = true;
837 self.0.ready.notify_one();
838 }
839}
840
841struct AsyncWriter {
842 tx: WriterTx,
843 metrics: AsyncWriterMetrics,
844 skip_wait: bool,
848 defer_wake: bool,
850 wake_pending: bool,
852 backpressure_wait: Duration,
855}
856
857enum Admission {
858 Done,
859 Refused(QueuedFrame),
860}
861
862pub enum FrameBytes {
865 Owned(Vec<u8>),
866 Shared(rns_core::transport::types::PacketBytes),
867}
868
869impl std::ops::Deref for FrameBytes {
870 type Target = [u8];
871
872 fn deref(&self) -> &[u8] {
873 match self {
874 FrameBytes::Owned(bytes) => bytes,
875 FrameBytes::Shared(bytes) => bytes,
876 }
877 }
878}
879
880struct QueuedFrame {
881 data: FrameBytes,
882 reserved_bytes: usize,
883 completion: Option<crate::link_send::Completion>,
884}
885
886impl Writer for AsyncWriter {
887 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
888 self.enqueue(data, &mut None)
889 }
890
891 fn send_frame_deferred(&mut self, data: &[u8]) -> io::Result<()> {
892 self.defer_wake = true;
893 let result = self.enqueue(data, &mut None);
894 self.defer_wake = false;
895 result
896 }
897
898 fn send_shared_deferred(
899 &mut self,
900 data: &rns_core::transport::types::PacketBytes,
901 ) -> io::Result<()> {
902 self.defer_wake = true;
903 let result = self.enqueue_unconfirmed(FrameBytes::Shared(data.clone()));
904 self.defer_wake = false;
905 result
906 }
907
908 fn wake(&mut self) {
909 if std::mem::take(&mut self.wake_pending) {
910 self.tx.0.kick();
911 }
912 }
913
914 fn tick(&mut self) {
915 self.wake();
917 }
918
919 fn flush_pending(&mut self) {
920 self.wake();
922 }
923
924 fn enqueue_confirmed(
925 &mut self,
926 data: &[u8],
927 completion: &mut Option<crate::link_send::Completion>,
928 ) -> io::Result<()> {
929 self.enqueue(data, completion)
930 }
931}
932
933impl AsyncWriter {
934 fn enqueue_unconfirmed(&mut self, data: FrameBytes) -> io::Result<()> {
941 let mut frame = QueuedFrame {
942 data,
943 reserved_bytes: 0,
944 completion: None,
945 };
946 match self.try_admit(frame)? {
948 Admission::Done => return Ok(()),
949 Admission::Refused(returned) => frame = returned,
950 }
951 if self.skip_wait {
952 return self.refuse(frame);
953 }
954 self.wake();
957 let space = Arc::clone(&self.metrics.space);
958 let (lock, condvar) = &*space;
959 let mut guard = lock_or_recover(lock, "async writer space");
960 self.metrics.space_waiters.fetch_add(1, Ordering::SeqCst);
964 let deadline = Instant::now() + self.backpressure_wait;
965 let result = loop {
966 match self.try_admit(frame) {
967 Err(error) => break Err(error),
968 Ok(Admission::Done) => break Ok(()),
969 Ok(Admission::Refused(returned)) => frame = returned,
970 }
971 let now = Instant::now();
972 if now >= deadline {
973 self.skip_wait = true;
974 break self.refuse(frame);
975 }
976 guard = match condvar.wait_timeout(guard, deadline - now) {
977 Ok((guard, _)) => guard,
978 Err(poisoned) => poisoned.into_inner().0,
979 };
980 };
981 self.metrics.space_waiters.fetch_sub(1, Ordering::SeqCst);
982 drop(guard);
983 result
984 }
985
986 fn try_admit(&mut self, mut frame: QueuedFrame) -> io::Result<Admission> {
989 if !self.metrics.worker_alive() {
990 return Err(io::Error::new(
991 io::ErrorKind::BrokenPipe,
992 "interface writer worker is offline",
993 ));
994 }
995 if let Some(control) = &self.metrics.egress_control {
996 let bytes = crate::hdlc::framed_len(&frame.data);
997 if bytes > control.byte_limit || control.stalled() {
998 control.record_drop(bytes);
999 return Ok(Admission::Done);
1000 }
1001 if !control.try_reserve(bytes) {
1002 return Ok(Admission::Refused(frame));
1003 }
1004 frame.reserved_bytes = bytes;
1005 }
1006 self.metrics.queued_frames.fetch_add(1, Ordering::Relaxed);
1007 match self.tx.try_send(frame, !self.defer_wake) {
1008 Ok(needs_wake) => {
1009 self.skip_wait = false;
1010 self.wake_pending |= needs_wake;
1011 Ok(Admission::Done)
1012 }
1013 Err(TrySendError::Full(mut returned)) => {
1014 self.metrics.queued_frames.fetch_sub(1, Ordering::Relaxed);
1015 if let Some(control) = &self.metrics.egress_control {
1016 control.release(returned.reserved_bytes);
1017 }
1018 returned.reserved_bytes = 0;
1019 Ok(Admission::Refused(returned))
1020 }
1021 Err(TrySendError::Disconnected(returned)) => {
1022 self.metrics.queued_frames.fetch_sub(1, Ordering::Relaxed);
1023 if let Some(control) = &self.metrics.egress_control {
1024 control.release(returned.reserved_bytes);
1025 }
1026 self.metrics.worker_alive.store(false, Ordering::Relaxed);
1027 Err(io::Error::new(
1028 io::ErrorKind::BrokenPipe,
1029 "interface writer worker disconnected",
1030 ))
1031 }
1032 }
1033 }
1034
1035 fn refuse(&self, frame: QueuedFrame) -> io::Result<()> {
1039 if let Some(control) = &self.metrics.egress_control {
1040 let bytes = crate::hdlc::framed_len(&frame.data);
1041 if self.metrics.queued_frames() < self.tx.0.capacity {
1042 control.record_drop(bytes);
1043 return Ok(());
1044 }
1045 }
1046 Err(io::Error::new(
1047 io::ErrorKind::WouldBlock,
1048 "interface writer queue is full",
1049 ))
1050 }
1051
1052 fn enqueue(
1053 &mut self,
1054 data: &[u8],
1055 completion: &mut Option<crate::link_send::Completion>,
1056 ) -> io::Result<()> {
1057 if !self.metrics.worker_alive() {
1058 return Err(io::Error::new(
1059 io::ErrorKind::BrokenPipe,
1060 "interface writer worker is offline",
1061 ));
1062 }
1063
1064 if completion.is_none() {
1069 return self.enqueue_unconfirmed(FrameBytes::Owned(data.to_vec()));
1070 }
1071
1072 let reserved_bytes = if let Some(control) = &self.metrics.egress_control {
1073 let framed_bytes = crate::hdlc::framed_len(data);
1074 if !control.try_reserve(framed_bytes) {
1075 if completion.is_some() {
1076 if framed_bytes > control.byte_limit {
1077 return Err(io::Error::new(
1078 io::ErrorKind::InvalidInput,
1079 "frame exceeds interface byte budget",
1080 ));
1081 }
1082 return Err(io::Error::new(
1083 io::ErrorKind::WouldBlock,
1084 "interface egress budget is full",
1085 ));
1086 }
1087 control.record_drop(framed_bytes);
1088 return Ok(());
1089 }
1090 framed_bytes
1091 } else {
1092 0
1093 };
1094
1095 self.metrics.queued_frames.fetch_add(1, Ordering::Relaxed);
1097 match self.tx.try_send(
1098 QueuedFrame {
1099 data: FrameBytes::Owned(data.to_vec()),
1100 reserved_bytes,
1101 completion: completion.take(),
1102 },
1103 true,
1104 ) {
1105 Ok(_) => Ok(()),
1106 Err(TrySendError::Full(frame)) => {
1107 *completion = frame.completion;
1108 self.metrics.queued_frames.fetch_sub(1, Ordering::Relaxed);
1109 if let Some(control) = &self.metrics.egress_control {
1110 control.release(frame.reserved_bytes);
1111 }
1112 Err(io::Error::new(
1113 io::ErrorKind::WouldBlock,
1114 "interface writer queue is full",
1115 ))
1116 }
1117 Err(TrySendError::Disconnected(frame)) => {
1118 *completion = frame.completion;
1119 self.metrics.queued_frames.fetch_sub(1, Ordering::Relaxed);
1120 if let Some(control) = &self.metrics.egress_control {
1121 control.release(frame.reserved_bytes);
1122 }
1123 self.metrics.worker_alive.store(false, Ordering::Relaxed);
1124 Err(io::Error::new(
1125 io::ErrorKind::BrokenPipe,
1126 "interface writer worker disconnected",
1127 ))
1128 }
1129 }
1130 }
1131}
1132
1133pub fn wrap_async_writer(
1134 writer: Box<dyn Writer>,
1135 interface_id: InterfaceId,
1136 interface_name: &str,
1137 event_tx: EventSender,
1138 queue_capacity: usize,
1139) -> (Box<dyn Writer>, AsyncWriterMetrics) {
1140 wrap_async_writer_with_wait(
1141 writer,
1142 interface_id,
1143 interface_name,
1144 event_tx,
1145 queue_capacity,
1146 ASYNC_WRITER_BACKPRESSURE_WAIT,
1147 )
1148}
1149
1150fn wrap_async_writer_with_wait(
1153 writer: Box<dyn Writer>,
1154 interface_id: InterfaceId,
1155 interface_name: &str,
1156 event_tx: EventSender,
1157 queue_capacity: usize,
1158 backpressure_wait: Duration,
1159) -> (Box<dyn Writer>, AsyncWriterMetrics) {
1160 if let Some(fd) = writer.loop_socket() {
1161 return loop_writer::wrap(writer, fd, interface_id, interface_name, event_tx);
1162 }
1163 if writer.sends_inline() {
1164 let metrics = AsyncWriterMetrics {
1165 queued_frames: Arc::default(),
1166 worker_alive: Arc::new(AtomicBool::new(true)),
1167 egress_control: None,
1168 space: Arc::default(),
1169 space_waiters: Arc::default(),
1170 };
1171 return (writer, metrics);
1172 }
1173 let egress_control = writer.egress_control();
1174 let queue = WriterQueue::new(queue_capacity);
1175 let (tx, rx) = (WriterTx(Arc::clone(&queue)), queue);
1176 let metrics = AsyncWriterMetrics {
1177 queued_frames: Arc::new(AtomicUsize::new(0)),
1178 worker_alive: Arc::new(AtomicBool::new(true)),
1179 egress_control,
1180 space: Arc::default(),
1181 space_waiters: Arc::default(),
1182 };
1183 let metrics_thread = metrics.clone();
1184 let name = interface_name.to_string();
1185
1186 let spawn_result = thread::Builder::new()
1187 .name(format!("iface-writer-{}", interface_id.0))
1188 .spawn(move || async_writer_loop(writer, rx, interface_id, name, event_tx, metrics_thread));
1189
1190 if let Err(err) = spawn_result {
1191 metrics.worker_alive.store(false, Ordering::Relaxed);
1192 log::error!(
1193 "[{}:{}] failed to spawn async writer thread: {}",
1194 interface_name,
1195 interface_id.0,
1196 err
1197 );
1198 return (Box::new(DirectWriterFallback), metrics);
1199 }
1200
1201 (
1202 Box::new(AsyncWriter {
1203 tx,
1204 metrics: metrics.clone(),
1205 skip_wait: false,
1206 defer_wake: false,
1207 wake_pending: false,
1208 backpressure_wait,
1209 }),
1210 metrics,
1211 )
1212}
1213
1214struct DirectWriterFallback;
1215
1216impl Writer for DirectWriterFallback {
1217 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
1218 Err(io::Error::other("interface writer worker unavailable"))
1219 }
1220}
1221
1222fn async_writer_loop(
1223 mut writer: Box<dyn Writer>,
1224 rx: Arc<WriterQueue>,
1225 interface_id: InterfaceId,
1226 interface_name: String,
1227 event_tx: EventSender,
1228 metrics: AsyncWriterMetrics,
1229) {
1230 let mut batch = Vec::new();
1231 while rx.recv_all(&mut batch) {
1232 let queued = std::mem::take(&mut batch);
1233 let reserved_bytes = queued.iter().map(|frame| frame.reserved_bytes).sum();
1234 metrics
1235 .queued_frames
1236 .fetch_sub(queued.len(), Ordering::Relaxed);
1237 metrics.notify_space();
1238 if event_tx.link_send_pool().in_flight() > 0 {
1239 let _ = event_tx.try_send(crate::event::Event::LinkWriterReady);
1240 }
1241
1242 let mut result = Ok(());
1243 if queued.iter().any(|frame| frame.completion.is_some()) {
1244 for mut frame in queued {
1245 let confirmed = frame.completion.is_some();
1246 if frame.completion.as_ref().is_some_and(|c| c.is_finished()) {
1247 result = Err(io::Error::new(
1248 io::ErrorKind::Interrupted,
1249 "transmission cancelled by shutdown",
1250 ));
1251 }
1252 if result.is_ok() {
1253 result = if frame.completion.is_some() {
1254 writer.send_frame_confirmed(&frame.data)
1255 } else {
1256 writer.send_frame(&frame.data)
1257 };
1258 while result
1259 .as_ref()
1260 .is_err_and(|e| e.kind() == io::ErrorKind::WouldBlock)
1261 {
1262 if frame.completion.as_ref().is_some_and(|c| c.is_finished()) {
1263 result = Err(io::Error::new(
1264 io::ErrorKind::Interrupted,
1265 "transmission cancelled by shutdown",
1266 ));
1267 break;
1268 }
1269 thread::sleep(Duration::from_millis(1));
1270 result = writer.flush();
1271 }
1272 }
1273 if let Some(completion) = frame.completion.take() {
1274 completion.finish(
1275 result.as_ref().map(|_| ()).map_err(|e| {
1276 crate::link_send::LinkSendError::WriteFailed(e.to_string())
1277 }),
1278 );
1279 }
1280 if confirmed
1283 && result.as_ref().is_err_and(|error| {
1284 matches!(
1285 error.kind(),
1286 io::ErrorKind::PermissionDenied
1287 | io::ErrorKind::InvalidInput
1288 | io::ErrorKind::Unsupported
1289 )
1290 })
1291 {
1292 result = Ok(());
1293 }
1294 }
1295 } else {
1296 let frames: Vec<_> = queued.into_iter().map(|frame| frame.data).collect();
1297 result = writer.send_frames(&frames);
1298 }
1299 while result
1300 .as_ref()
1301 .is_err_and(|error| error.kind() == io::ErrorKind::WouldBlock)
1302 {
1303 thread::sleep(Duration::from_millis(1));
1304 result = writer.flush();
1305 }
1306
1307 if let Some(control) = &metrics.egress_control {
1308 control.release(reserved_bytes);
1309 }
1310 metrics.notify_space();
1311 if event_tx.link_send_pool().in_flight() > 0 {
1312 let _ = event_tx.try_send(crate::event::Event::LinkWriterReady);
1313 }
1314
1315 if let Err(err) = result {
1316 metrics.worker_alive.store(false, Ordering::Relaxed);
1317 log::warn!(
1318 "[{}:{}] async writer exiting after send failure: {}",
1319 interface_name,
1320 interface_id.0,
1321 err
1322 );
1323 writer.shutdown();
1324 rx.close_receiver();
1325 metrics.notify_space();
1326 let _ = event_tx.send(crate::event::Event::InterfaceDown(interface_id));
1327 return;
1328 }
1329 }
1330
1331 rx.close_receiver();
1332 metrics.worker_alive.store(false, Ordering::Relaxed);
1333 metrics.notify_space();
1334}
1335
1336pub use crate::common::interface_stats::{InterfaceStats, TrafficRates, ANNOUNCE_SAMPLE_MAX};
1337
1338use crate::common::management::InterfaceStatusView;
1339
1340pub struct InterfaceEntry {
1342 pub id: InterfaceId,
1343 pub info: InterfaceInfo,
1344 pub writer: Box<dyn Writer>,
1345 pub async_writer_metrics: Option<AsyncWriterMetrics>,
1346 pub enabled: bool,
1348 pub online: bool,
1349 pub dynamic: bool,
1352 pub ifac: Option<IfacState>,
1354 pub stats: InterfaceStats,
1356 pub interface_type: String,
1358 pub send_retry_at: Option<Instant>,
1360 pub send_retry_backoff: Duration,
1362}
1363
1364#[allow(clippy::large_enum_variant)]
1367pub enum StartResult {
1368 Simple {
1370 id: InterfaceId,
1371 info: InterfaceInfo,
1372 writer: Box<dyn Writer>,
1373 interface_type_name: String,
1374 control: Option<ListenerControl>,
1375 },
1376 Listener { control: Option<ListenerControl> },
1378 Multi {
1380 subinterfaces: Vec<SubInterface>,
1381 control: Option<ListenerControl>,
1382 },
1383}
1384
1385pub struct SubInterface {
1387 pub id: InterfaceId,
1388 pub info: InterfaceInfo,
1389 pub writer: Box<dyn Writer>,
1390 pub interface_type_name: String,
1391}
1392
1393pub struct StartContext {
1395 pub tx: EventSender,
1396 pub next_dynamic_id: Arc<AtomicU64>,
1397 pub mode: u8,
1398 pub gravity: i64,
1399 pub recursive_prs: bool,
1400 pub announces_from_internal: bool,
1401 pub announces_to_internal: Option<bool>,
1402 pub ingress_control: rns_core::transport::types::IngressControlConfig,
1403 pub stamp_costs: rns_core::transport::types::InterfaceStampCosts,
1405 pub ifac: Option<IfacState>,
1406 pub underlay_mark: Option<u32>,
1408}
1409
1410pub trait InterfaceConfigData: Send + Any {
1412 fn as_any(&self) -> &dyn Any;
1413 fn into_any(self: Box<Self>) -> Box<dyn Any>;
1414}
1415
1416pub struct ConfigSection<'a> {
1418 pub params: &'a HashMap<String, String>,
1419 pub children: &'a [crate::config::ParsedSubinterface],
1420}
1421
1422#[derive(Debug, Clone)]
1423pub struct DynamicInterfaceTemplate {
1424 pub parent_id: InterfaceId,
1425 pub interface_type: String,
1426 pub ifac: Option<IfacState>,
1427 pub mode: u8,
1428 pub gravity: i64,
1429 pub recursive_prs: bool,
1430 pub announces_from_internal: bool,
1431 pub announces_to_internal: Option<bool>,
1432 pub stamp_costs: rns_core::transport::types::InterfaceStampCosts,
1433}
1434
1435impl DynamicInterfaceTemplate {
1436 pub fn registration(
1437 &self,
1438 mut info: InterfaceInfo,
1439 ) -> crate::event::DynamicInterfaceRegistration {
1440 info.mode = self.mode;
1441 info.gravity = self.gravity;
1442 info.recursive_prs = self.recursive_prs;
1443 info.announces_from_internal = self.announces_from_internal;
1444 info.announces_to_internal = self.announces_to_internal;
1445 info.stamp_costs = self.stamp_costs;
1446 crate::event::DynamicInterfaceRegistration {
1447 info,
1448 interface_type: self.interface_type.clone(),
1449 parent_id: self.parent_id,
1450 telemetry: crate::event::InterfaceTelemetry::default(),
1451 ifac: self.ifac.clone(),
1452 }
1453 }
1454}
1455
1456impl<T: Send + 'static> InterfaceConfigData for T {
1457 fn as_any(&self) -> &dyn Any {
1458 self
1459 }
1460
1461 fn into_any(self: Box<Self>) -> Box<dyn Any> {
1462 self
1463 }
1464}
1465
1466pub trait InterfaceFactory: Send + Sync {
1468 fn type_name(&self) -> &str;
1470
1471 fn default_ifac_size(&self) -> usize {
1473 16
1474 }
1475
1476 fn parse_config(
1478 &self,
1479 name: &str,
1480 id: InterfaceId,
1481 params: &HashMap<String, String>,
1482 ) -> Result<Box<dyn InterfaceConfigData>, String>;
1483
1484 fn parse_config_section(
1487 &self,
1488 name: &str,
1489 id: InterfaceId,
1490 section: ConfigSection<'_>,
1491 ) -> Result<Box<dyn InterfaceConfigData>, String> {
1492 self.parse_config(name, id, section.params)
1493 }
1494
1495 fn start(
1497 &self,
1498 config: Box<dyn InterfaceConfigData>,
1499 ctx: StartContext,
1500 ) -> io::Result<StartResult>;
1501}
1502
1503impl InterfaceStatusView for InterfaceEntry {
1504 fn id(&self) -> InterfaceId {
1505 self.id
1506 }
1507 fn info(&self) -> &InterfaceInfo {
1508 &self.info
1509 }
1510 fn online(&self) -> bool {
1511 self.online
1512 }
1513 fn stats(&self) -> &InterfaceStats {
1514 &self.stats
1515 }
1516 fn tx_drops(&self) -> u64 {
1517 self.async_writer_metrics
1518 .as_ref()
1519 .map(AsyncWriterMetrics::tx_drops)
1520 .unwrap_or(0)
1521 }
1522 fn tx_dropped_bytes(&self) -> u64 {
1523 self.async_writer_metrics
1524 .as_ref()
1525 .map(AsyncWriterMetrics::tx_dropped_bytes)
1526 .unwrap_or(0)
1527 }
1528 fn tx_stalled(&self) -> bool {
1529 self.async_writer_metrics
1530 .as_ref()
1531 .is_some_and(AsyncWriterMetrics::tx_stalled)
1532 }
1533 fn tx_buffered(&self) -> usize {
1534 self.async_writer_metrics
1535 .as_ref()
1536 .map(AsyncWriterMetrics::tx_buffered)
1537 .unwrap_or(0)
1538 }
1539}
1540
1541#[cfg(test)]
1542mod tests {
1543 use super::*;
1544
1545 #[test]
1546 fn tcp_interfaces_take_upstream_bitrate_and_fixed_mtu_keys() {
1547 let params = |pairs: &[(&str, &str)]| -> HashMap<String, String> {
1548 pairs
1549 .iter()
1550 .map(|(k, v)| (k.to_string(), v.to_string()))
1551 .collect()
1552 };
1553 for (factory, type_name) in [
1554 (
1555 &tcp::TcpClientFactory as &dyn InterfaceFactory,
1556 "TCPClientInterface",
1557 ),
1558 (&tcp_server::TcpServerFactory, "TCPServerInterface"),
1559 ] {
1560 assert_eq!(factory.type_name(), type_name);
1561 let rates = |pairs: &[(&str, &str)]| {
1562 let config = factory
1563 .parse_config("bench", InterfaceId(1), ¶ms(pairs))
1564 .map_err(|error| error.to_string())?;
1565 let any = config.into_any();
1566 Ok::<_, String>(
1567 if let Some(c) = any.downcast_ref::<tcp::TcpClientConfig>() {
1568 (c.bitrate, c.fixed_mtu)
1569 } else {
1570 let c = any.downcast_ref::<tcp_server::TcpServerConfig>().unwrap();
1571 (c.bitrate, c.fixed_mtu)
1572 },
1573 )
1574 };
1575 assert_eq!(rates(&[]), Ok((None, None)));
1576 assert_eq!(
1577 rates(&[("bitrate", "1000000000"), ("fixed_mtu", "500")]),
1578 Ok((Some(1_000_000_000), Some(500)))
1579 );
1580 assert_eq!(
1581 rates(&[("fixed_mtu", "499")]),
1582 Err("Configured MTU of 499 bytes is too small".to_string())
1583 );
1584 assert!(rates(&[("bitrate", "fast")]).is_err());
1585 }
1586 }
1587
1588 #[test]
1589 fn tcp_interfaces_take_the_flush_after_key() {
1590 let params = |pairs: &[(&str, &str)]| -> HashMap<String, String> {
1591 pairs
1592 .iter()
1593 .map(|(k, v)| (k.to_string(), v.to_string()))
1594 .collect()
1595 };
1596 for factory in [
1597 &tcp::TcpClientFactory as &dyn InterfaceFactory,
1598 &tcp_server::TcpServerFactory,
1599 ] {
1600 let flush_after = |pairs: &[(&str, &str)]| {
1601 let config = factory
1602 .parse_config("bench", InterfaceId(1), ¶ms(pairs))
1603 .map_err(|error| error.to_string())?;
1604 let any = config.into_any();
1605 Ok::<_, String>(
1606 if let Some(c) = any.downcast_ref::<tcp::TcpClientConfig>() {
1607 c.flush_after
1608 } else {
1609 any.downcast_ref::<tcp_server::TcpServerConfig>()
1610 .unwrap()
1611 .flush_after
1612 },
1613 )
1614 };
1615 assert_eq!(flush_after(&[]), Ok(DEFAULT_FLUSH_AFTER));
1616 assert_eq!(flush_after(&[("flush_after_us", "off")]), Ok(None));
1617 assert_eq!(
1618 flush_after(&[("flush_after_us", "25")]),
1619 Ok(Some(Duration::from_micros(25)))
1620 );
1621 assert!(flush_after(&[("flush_after_us", "soon")]).is_err());
1622 }
1623 }
1624
1625 #[test]
1626 fn tcp_hardware_mtu_follows_fixed_mtu_then_the_bitrate_tier() {
1627 assert_eq!(tcp_hardware_mtu(None, None), TCP_DEFAULT_MTU);
1628 assert_eq!(tcp_hardware_mtu(Some(10_000_000), None), 16_384);
1629 assert_eq!(tcp_hardware_mtu(Some(1_000_000_000), None), 524_288);
1630 assert_eq!(tcp_hardware_mtu(Some(1_000_000_000), Some(500)), 500);
1631 assert_eq!(tcp_hardware_mtu(None, Some(1_200)), 1_200);
1632 assert_eq!(
1634 tcp_hardware_mtu(Some(9_600), None),
1635 rns_core::constants::MTU as u32
1636 );
1637 }
1638
1639 #[test]
1640 fn shared_medium_hint_matches_interface_class_defaults() {
1641 for interface_type in [
1642 "AX25KISSInterface",
1643 "KISSInterface",
1644 "PipeInterface",
1645 "RNodeInterface",
1646 "RNodeMultiInterface",
1647 "SerialInterface",
1648 "UDPInterface",
1649 ] {
1650 assert!(shared_medium_hint(interface_type), "{interface_type}");
1651 }
1652 for interface_type in [
1653 "AutoInterface",
1654 "BackboneInterface",
1655 "I2PInterface",
1656 "LocalInterface",
1657 "TCPClientInterface",
1658 "TCPServerInterface",
1659 ] {
1660 assert!(!shared_medium_hint(interface_type), "{interface_type}");
1661 }
1662 }
1663 use crate::event::Event;
1664 use rns_core::constants;
1665 use std::sync::mpsc;
1666
1667 struct MockWriter {
1668 sent: Vec<Vec<u8>>,
1669 }
1670
1671 impl MockWriter {
1672 fn new() -> Self {
1673 MockWriter { sent: Vec::new() }
1674 }
1675 }
1676
1677 impl Writer for MockWriter {
1678 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
1679 self.sent.push(data.to_vec());
1680 Ok(())
1681 }
1682 }
1683
1684 #[test]
1685 fn interface_entry_construction() {
1686 let entry = InterfaceEntry {
1687 id: InterfaceId(1),
1688 info: InterfaceInfo {
1689 stamp_costs: Default::default(),
1690 id: InterfaceId(1),
1691 name: String::new(),
1692 mode: constants::MODE_FULL,
1693 gravity: 0,
1694 recursive_prs: false,
1695 announces_from_internal: true,
1696 announces_to_internal: None,
1697 out_capable: true,
1698 in_capable: true,
1699 bitrate: None,
1700 airtime_profile: None,
1701 announce_rate_target: None,
1702 announce_rate_grace: 0,
1703 announce_rate_penalty: 0.0,
1704 announce_cap: constants::ANNOUNCE_CAP,
1705 is_local_client: false,
1706 wants_tunnel: false,
1707 tunnel_id: None,
1708 mtu: constants::MTU as u32,
1709 ia_freq: 0.0,
1710 ip_freq: 0.0,
1711 op_freq: 0.0,
1712 op_samples: 0,
1713 started: 0.0,
1714 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
1715 },
1716 writer: Box::new(MockWriter::new()),
1717 async_writer_metrics: None,
1718 enabled: true,
1719 online: false,
1720 dynamic: false,
1721 ifac: None,
1722 stats: InterfaceStats::default(),
1723 interface_type: String::new(),
1724 send_retry_at: None,
1725 send_retry_backoff: Duration::ZERO,
1726 };
1727 assert_eq!(entry.id, InterfaceId(1));
1728 assert!(!entry.online);
1729 assert!(!entry.dynamic);
1730 }
1731
1732 #[test]
1733 fn dynamic_registration_inherits_announces_to_internal() {
1734 let template = DynamicInterfaceTemplate {
1735 parent_id: InterfaceId(7),
1736 interface_type: "dynamic-test".into(),
1737 ifac: None,
1738 mode: constants::MODE_GATEWAY,
1739 gravity: -3,
1740 recursive_prs: true,
1741 announces_from_internal: false,
1742 announces_to_internal: Some(true),
1743 stamp_costs: rns_core::transport::types::InterfaceStampCosts {
1744 destination_cost: Some(rns_core::stamp::StampCost::from_whole(30)),
1745 pr_cost: None,
1746 },
1747 };
1748 let info = InterfaceInfo {
1749 stamp_costs: Default::default(),
1750 id: InterfaceId(8),
1751 name: "child".into(),
1752 mode: constants::MODE_FULL,
1753 gravity: 0,
1754 recursive_prs: false,
1755 announces_from_internal: true,
1756 announces_to_internal: None,
1757 out_capable: true,
1758 in_capable: true,
1759 bitrate: None,
1760 airtime_profile: None,
1761 announce_rate_target: None,
1762 announce_rate_grace: 0,
1763 announce_rate_penalty: 0.0,
1764 announce_cap: constants::ANNOUNCE_CAP,
1765 is_local_client: false,
1766 wants_tunnel: false,
1767 tunnel_id: None,
1768 mtu: constants::MTU as u32,
1769 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
1770 ia_freq: 0.0,
1771 ip_freq: 0.0,
1772 op_freq: 0.0,
1773 op_samples: 0,
1774 started: 0.0,
1775 };
1776
1777 let registration = template.registration(info);
1778
1779 assert_eq!(registration.info.mode, constants::MODE_GATEWAY);
1780 assert_eq!(registration.info.gravity, -3);
1781 assert!(registration.info.recursive_prs);
1782 assert!(!registration.info.announces_from_internal);
1783 assert_eq!(registration.info.announces_to_internal, Some(true));
1784 assert_eq!(
1785 registration.info.stamp_costs.destination_cost,
1786 Some(rns_core::stamp::StampCost::from_whole(30))
1787 );
1788 }
1789
1790 #[test]
1791 fn mock_writer_captures_bytes() {
1792 let mut writer = MockWriter::new();
1793 writer.send_frame(b"hello").unwrap();
1794 writer.send_frame(b"world").unwrap();
1795 assert_eq!(writer.sent.len(), 2);
1796 assert_eq!(writer.sent[0], b"hello");
1797 assert_eq!(writer.sent[1], b"world");
1798 }
1799
1800 #[test]
1801 fn writer_send_frame_produces_output() {
1802 let mut writer = MockWriter::new();
1803 let data = vec![0x01, 0x02, 0x03];
1804 writer.send_frame(&data).unwrap();
1805 assert_eq!(writer.sent[0], data);
1806 }
1807
1808 struct BlockingWriter {
1809 entered_tx: mpsc::Sender<()>,
1810 release_rx: mpsc::Receiver<()>,
1811 }
1812
1813 #[test]
1814 fn confirmed_writer_waits_for_flush_and_reports_write_failures() {
1815 use crate::link_send::{Completion, LinkSendError, SendPool};
1816 use futures::FutureExt;
1817 struct FlushWriter {
1818 entered: mpsc::Sender<()>,
1819 release: mpsc::Receiver<()>,
1820 writes: Arc<AtomicUsize>,
1821 }
1822 impl Writer for FlushWriter {
1823 fn send_frame(&mut self, _: &[u8]) -> io::Result<()> {
1824 self.writes.fetch_add(1, Ordering::SeqCst);
1825 Err(io::ErrorKind::WouldBlock.into())
1826 }
1827 fn flush(&mut self) -> io::Result<()> {
1828 self.entered.send(()).unwrap();
1829 self.release.recv().unwrap();
1830 Ok(())
1831 }
1832 }
1833 let (event_tx, _rx) = crate::event::channel();
1834 let (entered, entered_rx) = mpsc::channel();
1835 let (release_tx, release) = mpsc::channel();
1836 let writes = Arc::new(AtomicUsize::new(0));
1837 let pool = SendPool::new(1);
1838 let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
1839 let (mut writer, _) = wrap_async_writer(
1840 Box::new(FlushWriter {
1841 entered,
1842 release,
1843 writes: writes.clone(),
1844 }),
1845 InterfaceId(999),
1846 "flush test",
1847 event_tx.clone(),
1848 1,
1849 );
1850 writer
1851 .enqueue_confirmed(b"once", &mut Some(completion))
1852 .unwrap();
1853 entered_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1854 assert!((&mut receipt).now_or_never().is_none());
1855 release_tx.send(()).unwrap();
1856 receipt.wait().unwrap();
1857 assert_eq!(
1858 writes.load(Ordering::SeqCst),
1859 1,
1860 "flush must not resubmit the frame"
1861 );
1862
1863 let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
1864 let (mut writer, _) = wrap_async_writer(
1865 Box::new(FailingWriter {
1866 shutdown_called: Arc::new(AtomicBool::new(false)),
1867 }),
1868 InterfaceId(998),
1869 "failure test",
1870 event_tx,
1871 1,
1872 );
1873 writer
1874 .enqueue_confirmed(b"fail", &mut Some(completion))
1875 .unwrap();
1876 assert_eq!(
1877 receipt.wait(),
1878 Err(LinkSendError::WriteFailed("boom".into()))
1879 );
1880 }
1881
1882 impl Writer for BlockingWriter {
1883 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
1884 let _ = self.entered_tx.send(());
1885 let _ = self.release_rx.recv();
1886 Ok(())
1887 }
1888 }
1889
1890 struct EgressControlledBlockingWriter {
1891 inner: BlockingWriter,
1892 control: EgressControl,
1893 }
1894
1895 impl Writer for EgressControlledBlockingWriter {
1896 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
1897 self.inner.send_frame(data)
1898 }
1899
1900 fn egress_control(&self) -> Option<EgressControl> {
1901 Some(self.control.clone())
1902 }
1903 }
1904
1905 struct FailingWriter {
1906 shutdown_called: Arc<AtomicBool>,
1907 }
1908
1909 struct RecordingWriter {
1910 sent_tx: mpsc::Sender<Vec<u8>>,
1911 }
1912
1913 impl Writer for RecordingWriter {
1914 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
1915 self.sent_tx
1916 .send(data.to_vec())
1917 .map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "test receiver closed"))
1918 }
1919 }
1920
1921 struct BatchRecordingWriter {
1922 batch_tx: mpsc::Sender<usize>,
1923 first_release: Option<mpsc::Receiver<()>>,
1924 }
1925
1926 impl Writer for BatchRecordingWriter {
1927 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
1928 unreachable!("the async worker must use batch delivery")
1929 }
1930
1931 fn send_frames(&mut self, frames: &[crate::interface::FrameBytes]) -> io::Result<()> {
1932 self.batch_tx.send(frames.len()).unwrap();
1933 if let Some(release) = self.first_release.take() {
1934 release.recv().unwrap();
1935 }
1936 Ok(())
1937 }
1938 }
1939
1940 impl Writer for FailingWriter {
1941 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
1942 Err(io::Error::new(io::ErrorKind::BrokenPipe, "boom"))
1943 }
1944
1945 fn shutdown(&mut self) {
1946 self.shutdown_called.store(true, Ordering::Relaxed);
1947 }
1948 }
1949
1950 #[test]
1951 fn async_writer_returns_wouldblock_when_queue_is_full() {
1952 let (event_tx, _event_rx) = crate::event::channel();
1953 let (entered_tx, entered_rx) = mpsc::channel();
1954 let (release_tx, release_rx) = mpsc::channel();
1955 let (mut writer, metrics) = wrap_async_writer(
1956 Box::new(BlockingWriter {
1957 entered_tx,
1958 release_rx,
1959 }),
1960 InterfaceId(7),
1961 "test",
1962 event_tx,
1963 1,
1964 );
1965
1966 writer.send_frame(&[1]).unwrap();
1967 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
1968 writer.send_frame(&[2]).unwrap();
1969 assert_eq!(metrics.queued_frames(), 1);
1970 let err = writer.send_frame(&[3]).unwrap_err();
1971 assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
1972
1973 release_tx.send(()).unwrap();
1974 entered_rx
1975 .recv_timeout(Duration::from_secs(1))
1976 .expect("the queued frame must wake the writer without another send trigger");
1977 assert_eq!(metrics.queued_frames(), 0);
1978 release_tx.send(()).unwrap();
1979 }
1980
1981 #[test]
1982 fn deferred_sends_are_delivered_on_wake_or_by_the_tick_backstop() {
1983 struct Recorder(mpsc::Sender<Vec<u8>>);
1984 impl Writer for Recorder {
1985 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
1986 self.0.send(data.to_vec()).unwrap();
1987 Ok(())
1988 }
1989 }
1990 let (event_tx, _event_rx) = crate::event::channel();
1991 let (sent_tx, sent_rx) = mpsc::channel();
1992 let (mut writer, _metrics) = wrap_async_writer(
1993 Box::new(Recorder(sent_tx)),
1994 InterfaceId(9),
1995 "test",
1996 event_tx,
1997 8,
1998 );
1999 std::thread::sleep(Duration::from_millis(20));
2001 writer.send_frame_deferred(&[1]).unwrap();
2002 writer.send_frame_deferred(&[2]).unwrap();
2003 writer.wake();
2004 assert_eq!(
2005 sent_rx.recv_timeout(Duration::from_secs(1)).unwrap(),
2006 vec![1]
2007 );
2008 assert_eq!(
2009 sent_rx.recv_timeout(Duration::from_secs(1)).unwrap(),
2010 vec![2]
2011 );
2012
2013 std::thread::sleep(Duration::from_millis(20));
2014 writer.send_frame_deferred(&[3]).unwrap();
2015 assert!(sent_rx.recv_timeout(Duration::from_millis(100)).is_err());
2017 writer.tick();
2019 assert_eq!(
2020 sent_rx.recv_timeout(Duration::from_secs(1)).unwrap(),
2021 vec![3]
2022 );
2023 writer.send_frame_deferred(&[4]).unwrap();
2024 writer.flush_pending();
2025 assert_eq!(
2026 sent_rx.recv_timeout(Duration::from_secs(1)).unwrap(),
2027 vec![4]
2028 );
2029 }
2030
2031 #[test]
2032 fn async_writer_waits_briefly_for_space_then_fails_fast_while_stalled() {
2033 let (event_tx, _event_rx) = crate::event::channel();
2034 let (entered_tx, entered_rx) = mpsc::channel();
2035 let (release_tx, release_rx) = mpsc::channel();
2036 let wait = Duration::from_millis(500);
2040 let (mut writer, metrics) = wrap_async_writer_with_wait(
2041 Box::new(BlockingWriter {
2042 entered_tx,
2043 release_rx,
2044 }),
2045 InterfaceId(8),
2046 "test",
2047 event_tx,
2048 1,
2049 wait,
2050 );
2051 writer.send_frame(&[1]).unwrap();
2052 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
2053 writer.send_frame(&[2]).unwrap();
2054
2055 let release = release_tx.clone();
2058 let releaser = std::thread::spawn(move || {
2059 std::thread::sleep(Duration::from_millis(5));
2060 release.send(()).unwrap();
2061 });
2062 writer.send_frame(&[3]).unwrap();
2063 releaser.join().unwrap();
2064 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
2065 assert_eq!(metrics.queued_frames(), 1);
2066
2067 let started = Instant::now();
2070 let err = writer.send_frame(&[4]).unwrap_err();
2071 assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
2072 assert!(started.elapsed() >= wait);
2073 let started = Instant::now();
2074 let err = writer.send_frame(&[5]).unwrap_err();
2075 assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
2076 assert!(started.elapsed() < wait);
2077
2078 for _ in 0..3 {
2079 let _ = release_tx.send(());
2080 }
2081 }
2082
2083 #[test]
2084 fn async_writer_shares_exact_egress_byte_valve_and_stall_gate() {
2085 let (event_tx, _event_rx) = crate::event::channel();
2086 let (entered_tx, entered_rx) = mpsc::channel();
2087 let (release_tx, release_rx) = mpsc::channel();
2088 let control = EgressControl::new(4);
2089 let (mut writer, metrics) = wrap_async_writer(
2090 Box::new(EgressControlledBlockingWriter {
2091 inner: BlockingWriter {
2092 entered_tx,
2093 release_rx,
2094 },
2095 control: control.clone(),
2096 }),
2097 InterfaceId(8),
2098 "egress-test",
2099 event_tx,
2100 8,
2101 );
2102
2103 writer.send_frame(&[1, 2]).unwrap();
2105 entered_rx.recv_timeout(Duration::from_secs(1)).unwrap();
2106 assert_eq!(control.reserved_bytes.load(Ordering::Relaxed), 4);
2107 assert_eq!(metrics.tx_buffered(), 4);
2108
2109 writer.send_frame(&[3, 4]).unwrap();
2111 assert_eq!(control.reserved_bytes.load(Ordering::Relaxed), 4);
2112 assert_eq!(control.dropped_frames.load(Ordering::Relaxed), 1);
2113 assert_eq!(control.dropped_bytes.load(Ordering::Relaxed), 4);
2114 assert_eq!(metrics.tx_drops(), 1);
2115 assert_eq!(metrics.tx_dropped_bytes(), 4);
2116
2117 release_tx.send(()).unwrap();
2118 let deadline = Instant::now() + Duration::from_secs(1);
2119 while control.reserved_bytes.load(Ordering::Relaxed) != 0 {
2120 assert!(
2121 Instant::now() < deadline,
2122 "egress reservation did not drain"
2123 );
2124 thread::yield_now();
2125 }
2126
2127 control.set_stalled(true);
2128 assert!(metrics.tx_stalled());
2129 writer.send_frame(&[5]).unwrap();
2130 assert_eq!(control.dropped_frames.load(Ordering::Relaxed), 2);
2131 assert_eq!(control.dropped_bytes.load(Ordering::Relaxed), 7);
2132 assert_eq!(metrics.queued_frames(), 0);
2133 }
2134
2135 #[test]
2136 fn async_writer_reports_interface_down_after_worker_failure() {
2137 let (event_tx, event_rx) = crate::event::channel();
2138 let shutdown_called = Arc::new(AtomicBool::new(false));
2139 let (mut writer, metrics) = wrap_async_writer(
2140 Box::new(FailingWriter {
2141 shutdown_called: Arc::clone(&shutdown_called),
2142 }),
2143 InterfaceId(9),
2144 "fail",
2145 event_tx,
2146 2,
2147 );
2148
2149 writer.send_frame(&[1]).unwrap();
2150 let event = event_rx.recv_timeout(Duration::from_secs(1)).unwrap();
2151 assert!(matches!(event, Event::InterfaceDown(InterfaceId(9))));
2152 assert!(!metrics.worker_alive());
2153 assert!(shutdown_called.load(Ordering::Relaxed));
2154 }
2155
2156 #[test]
2157 fn async_writer_preserves_order_through_fifty_thousand_frame_burst() {
2158 const FRAME_COUNT: usize = 50_000;
2159 let (event_tx, _event_rx) = crate::event::channel();
2160 let (sent_tx, sent_rx) = mpsc::channel();
2161 let (mut writer, metrics) = wrap_async_writer(
2162 Box::new(RecordingWriter { sent_tx }),
2163 InterfaceId(11),
2164 "burst",
2165 event_tx,
2166 FRAME_COUNT,
2167 );
2168
2169 for index in 0..FRAME_COUNT {
2170 let payload = (index as u64).to_be_bytes();
2171 writer.send_frame(&payload).unwrap();
2172 }
2173
2174 for index in 0..FRAME_COUNT {
2175 let received = sent_rx.recv_timeout(Duration::from_secs(2)).unwrap();
2176 assert_eq!(received, (index as u64).to_be_bytes());
2177 }
2178 assert!(metrics.worker_alive());
2179 }
2180
2181 #[test]
2182 fn async_writer_coalesces_frames_waiting_behind_an_active_batch() {
2183 let (event_tx, _event_rx) = crate::event::channel();
2184 let (batch_tx, batch_rx) = mpsc::channel();
2185 let (release_tx, release_rx) = mpsc::channel();
2186 let (mut writer, _) = wrap_async_writer(
2187 Box::new(BatchRecordingWriter {
2188 batch_tx,
2189 first_release: Some(release_rx),
2190 }),
2191 InterfaceId(12),
2192 "batch",
2193 event_tx,
2194 16,
2195 );
2196
2197 writer.send_frame(&[0]).unwrap();
2198 assert_eq!(batch_rx.recv_timeout(Duration::from_secs(1)).unwrap(), 1);
2199 for index in 1..=10 {
2200 writer.send_frame(&[index]).unwrap();
2201 }
2202 release_tx.send(()).unwrap();
2203
2204 assert_eq!(batch_rx.recv_timeout(Duration::from_secs(1)).unwrap(), 10);
2205 }
2206}