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