1use std::io::{self, Read, Write};
12use std::net::{TcpListener, TcpStream};
13use std::sync::atomic::{AtomicU64, Ordering};
14use std::sync::{Arc, Mutex};
15use std::thread;
16use std::time::{Duration, Instant};
17
18use rns_core::constants;
19use rns_core::transport::types::{InterfaceId, InterfaceInfo};
20
21use crate::event::{Event, EventSender};
22use crate::hdlc;
23use crate::interface::transmit_buffer::TransmitBuffer;
24use crate::interface::{ListenerControl, Writer};
25
26pub(crate) const HW_MTU: u32 = 262_144;
28pub(crate) const BITRATE: u64 = 1_000_000_000;
29
30#[cfg(target_os = "android")]
31const CLIENT_SLEEP_PAUSE_TIMEOUT: Duration = Duration::from_secs(12);
32#[cfg(target_os = "android")]
33const PHY_KEEPALIVE_INTERVAL: Duration = Duration::from_secs(5);
34
35#[derive(Debug, Clone)]
37pub struct LocalServerConfig {
38 pub instance_name: String,
39 pub port: u16,
40 pub interface_id: InterfaceId,
41}
42
43impl Default for LocalServerConfig {
44 fn default() -> Self {
45 LocalServerConfig {
46 instance_name: "default".into(),
47 port: 37428,
48 interface_id: InterfaceId(0),
49 }
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct LocalClientConfig {
56 pub name: String,
57 pub instance_name: String,
58 pub port: u16,
59 pub interface_id: InterfaceId,
60 pub reconnect_wait: Duration,
61}
62
63impl Default for LocalClientConfig {
64 fn default() -> Self {
65 LocalClientConfig {
66 name: "Local shared instance".into(),
67 instance_name: "default".into(),
68 port: 37428,
69 interface_id: InterfaceId(0),
70 reconnect_wait: Duration::from_secs(8),
71 }
72 }
73}
74
75struct LocalWriter {
77 stream: TcpStream,
78 sleep_hold: Option<ClientSleepHold>,
79 transmit_buffer: TransmitBuffer,
80}
81
82impl Writer for LocalWriter {
83 fn send_frame_confirmed(&mut self, data: &[u8]) -> io::Result<()> {
84 if self
85 .sleep_hold
86 .as_ref()
87 .is_some_and(ClientSleepHold::should_drop_outbound)
88 {
89 return Err(io::Error::new(
90 io::ErrorKind::PermissionDenied,
91 "local client has paused transmission",
92 ));
93 }
94 append_and_drain(
95 &mut self.transmit_buffer,
96 &mut self.stream,
97 &[data.to_vec()],
98 )
99 }
100 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
101 self.send_frames(&[data.to_vec()])
102 }
103
104 fn send_frames(&mut self, frames: &[Vec<u8>]) -> io::Result<()> {
105 if self
106 .sleep_hold
107 .as_ref()
108 .is_some_and(ClientSleepHold::should_drop_outbound)
109 {
110 log::debug!("TX paused for LocalInterface client, dropping outbound packet");
111 return Ok(());
112 }
113 append_and_drain(&mut self.transmit_buffer, &mut self.stream, frames)
114 }
115}
116
117fn append_and_drain(
118 buffer: &mut TransmitBuffer,
119 writer: &mut impl Write,
120 frames: &[Vec<u8>],
121) -> io::Result<()> {
122 for frame in frames {
123 let _ = buffer.append(hdlc::frame(frame));
124 }
125 buffer.flush();
126 while buffer.sendable_bytes() > 0 {
127 if buffer.drain_to(writer)? == 0 {
128 return Err(io::Error::new(
129 io::ErrorKind::WriteZero,
130 "local writer made no progress",
131 ));
132 }
133 }
134 Ok(())
135}
136
137#[derive(Clone)]
138struct ClientSleepHold {
139 timeout: Duration,
140 deadline: Arc<Mutex<Instant>>,
141}
142
143impl ClientSleepHold {
144 #[cfg_attr(not(any(target_os = "android", test)), allow(dead_code))]
145 fn new(timeout: Duration) -> Self {
146 Self {
147 timeout,
148 deadline: Arc::new(Mutex::new(Instant::now() + timeout)),
149 }
150 }
151
152 fn refresh(&self) {
153 *lock_or_recover(&self.deadline) = Instant::now() + self.timeout;
154 }
155
156 fn should_drop_outbound(&self) -> bool {
157 Instant::now() > *lock_or_recover(&self.deadline)
158 }
159}
160
161fn lock_or_recover<T>(mutex: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
162 match mutex.lock() {
163 Ok(guard) => guard,
164 Err(poisoned) => poisoned.into_inner(),
165 }
166}
167
168fn android_client_sleep_hold() -> Option<ClientSleepHold> {
169 #[cfg(target_os = "android")]
170 {
171 Some(ClientSleepHold::new(CLIENT_SLEEP_PAUSE_TIMEOUT))
172 }
173 #[cfg(not(target_os = "android"))]
174 {
175 None
176 }
177}
178
179#[cfg_attr(not(any(target_os = "android", test)), allow(dead_code))]
180fn spawn_physical_keepalive_loop(
181 mut writer: Box<dyn Writer>,
182 interface_id: InterfaceId,
183 interface_name: String,
184 interval: Duration,
185) {
186 thread::Builder::new()
190 .name(format!("local-phy-keepalive-{}", interface_id.0))
191 .spawn(move || loop {
192 thread::sleep(interval);
193 if let Err(err) = writer.send_frame(&[]) {
194 log::debug!(
195 "[{}:{}] LocalInterface physical keepalive stopped: {}",
196 interface_name,
197 interface_id.0,
198 err
199 );
200 return;
201 }
202 })
203 .ok();
204}
205
206fn maybe_spawn_local_client_phy_keepalive(
207 stream: &LocalClientStream,
208 interface_id: InterfaceId,
209 interface_name: &str,
210) -> io::Result<()> {
211 #[cfg(target_os = "android")]
212 {
213 let writer = local_client_stream_writer(stream)?;
214 spawn_physical_keepalive_loop(
215 writer,
216 interface_id,
217 interface_name.to_string(),
218 PHY_KEEPALIVE_INTERVAL,
219 );
220 }
221
222 #[cfg(not(target_os = "android"))]
223 {
224 let _ = (stream, interface_id, interface_name);
225 }
226
227 Ok(())
228}
229
230#[cfg(target_os = "linux")]
231mod unix_socket {
232 use std::io;
233 use std::os::linux::net::SocketAddrExt;
234 use std::os::unix::net::{SocketAddr, UnixListener, UnixStream};
235
236 fn abstract_addr(instance_name: &str) -> io::Result<SocketAddr> {
237 SocketAddr::from_abstract_name(format!("rns/{}", instance_name))
238 }
239
240 pub fn try_bind_unix(instance_name: &str) -> io::Result<UnixListener> {
242 let addr = abstract_addr(instance_name)?;
243 UnixListener::bind_addr(&addr)
244 }
245
246 pub fn try_connect_unix(instance_name: &str) -> io::Result<UnixStream> {
248 let addr = abstract_addr(instance_name)?;
249 UnixStream::connect_addr(&addr)
250 }
251}
252
253pub fn start_server(
259 config: LocalServerConfig,
260 tx: EventSender,
261 next_id: Arc<AtomicU64>,
262) -> io::Result<ListenerControl> {
263 start_server_with_template(config, tx, next_id, None)
264}
265
266fn start_server_with_template(
267 config: LocalServerConfig,
268 tx: EventSender,
269 next_id: Arc<AtomicU64>,
270 dynamic_template: Option<super::DynamicInterfaceTemplate>,
271) -> io::Result<ListenerControl> {
272 let control = ListenerControl::new();
273 #[cfg(target_os = "linux")]
275 {
276 match unix_socket::try_bind_unix(&config.instance_name) {
277 Ok(listener) => {
278 listener.set_nonblocking(true)?;
279 log::info!(
280 "Local server using Unix socket: rns/{}",
281 config.instance_name
282 );
283 let name = format!("rns/{}", config.instance_name);
284 let listener_control = control.clone();
285 thread::Builder::new()
286 .name("local-server".into())
287 .spawn(move || {
288 unix_server_loop(
289 listener,
290 name,
291 tx,
292 next_id,
293 listener_control,
294 dynamic_template,
295 );
296 })?;
297 return Ok(control);
298 }
299 Err(e) => {
300 log::info!("Unix socket bind failed ({}), falling back to TCP", e);
301 }
302 }
303 }
304
305 let addr = format!("127.0.0.1:{}", config.port);
307 let listener = TcpListener::bind(&addr)?;
308 listener.set_nonblocking(true)?;
309
310 log::info!("Local server listening on TCP {}", addr);
311
312 let listener_control = control.clone();
313 thread::Builder::new()
314 .name("local-server".into())
315 .spawn(move || {
316 tcp_server_loop(listener, tx, next_id, listener_control, dynamic_template);
317 })?;
318
319 Ok(control)
320}
321
322fn tcp_server_loop(
324 listener: TcpListener,
325 tx: EventSender,
326 next_id: Arc<AtomicU64>,
327 control: ListenerControl,
328 dynamic_template: Option<super::DynamicInterfaceTemplate>,
329) {
330 loop {
331 if control.should_stop() {
332 log::info!("Local TCP listener stopping");
333 return;
334 }
335
336 let stream_result = listener.accept().map(|(stream, _)| stream);
337 let stream = match stream_result {
338 Ok(s) => s,
339 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
340 thread::sleep(Duration::from_millis(50));
341 continue;
342 }
343 Err(e) => {
344 log::warn!("Local server accept failed: {}", e);
345 continue;
346 }
347 };
348
349 if let Err(e) = stream.set_nodelay(true) {
350 log::warn!("Local server set_nodelay failed: {}", e);
351 }
352
353 let client_id = InterfaceId(next_id.fetch_add(1, Ordering::Relaxed));
354 spawn_local_client_handler(stream, client_id, tx.clone(), dynamic_template.as_ref());
355 }
356}
357
358#[cfg(target_os = "linux")]
360fn unix_server_loop(
361 listener: std::os::unix::net::UnixListener,
362 name: String,
363 tx: EventSender,
364 next_id: Arc<AtomicU64>,
365 control: ListenerControl,
366 dynamic_template: Option<super::DynamicInterfaceTemplate>,
367) {
368 loop {
369 if control.should_stop() {
370 log::info!("[{}] Local Unix listener stopping", name);
371 return;
372 }
373
374 let stream_result = listener.accept().map(|(stream, _)| stream);
375 let stream = match stream_result {
376 Ok(s) => s,
377 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
378 thread::sleep(Duration::from_millis(50));
379 continue;
380 }
381 Err(e) => {
382 log::warn!("[{}] Local server accept failed: {}", name, e);
383 continue;
384 }
385 };
386
387 let client_id = InterfaceId(next_id.fetch_add(1, Ordering::Relaxed));
388
389 let writer_stream = match stream.try_clone() {
391 Ok(s) => s,
392 Err(e) => {
393 log::warn!("Local server clone failed: {}", e);
394 continue;
395 }
396 };
397
398 let sleep_hold = android_client_sleep_hold();
399 let info = make_local_interface_info(client_id);
400 let writer: Box<dyn Writer> = Box::new(UnixLocalWriter {
401 stream: writer_stream,
402 sleep_hold: sleep_hold.clone(),
403 transmit_buffer: TransmitBuffer::new(),
404 });
405
406 let event = if let Some(template) = &dynamic_template {
407 Event::DynamicInterfaceUp {
408 id: client_id,
409 writer,
410 registration: template.registration(info),
411 }
412 } else {
413 Event::InterfaceUp(client_id, Some(writer), Some(info))
414 };
415 if tx.send(event).is_err() {
416 return;
417 }
418
419 let client_tx = tx.clone();
420 thread::Builder::new()
421 .name(format!("local-unix-reader-{}", client_id.0))
422 .spawn(move || {
423 unix_reader_loop(stream, client_id, client_tx, sleep_hold);
424 })
425 .ok();
426 }
427}
428
429#[cfg(target_os = "linux")]
430struct UnixLocalWriter {
431 stream: std::os::unix::net::UnixStream,
432 sleep_hold: Option<ClientSleepHold>,
433 transmit_buffer: TransmitBuffer,
434}
435
436#[cfg(target_os = "linux")]
437impl Writer for UnixLocalWriter {
438 fn send_frame_confirmed(&mut self, data: &[u8]) -> io::Result<()> {
439 if self
440 .sleep_hold
441 .as_ref()
442 .is_some_and(ClientSleepHold::should_drop_outbound)
443 {
444 return Err(io::Error::new(
445 io::ErrorKind::PermissionDenied,
446 "local client has paused transmission",
447 ));
448 }
449 append_and_drain(
450 &mut self.transmit_buffer,
451 &mut self.stream,
452 &[data.to_vec()],
453 )
454 }
455 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
456 self.send_frames(&[data.to_vec()])
457 }
458
459 fn send_frames(&mut self, frames: &[Vec<u8>]) -> io::Result<()> {
460 if self
461 .sleep_hold
462 .as_ref()
463 .is_some_and(ClientSleepHold::should_drop_outbound)
464 {
465 log::debug!("TX paused for LocalInterface client, dropping outbound packet");
466 return Ok(());
467 }
468 append_and_drain(&mut self.transmit_buffer, &mut self.stream, frames)
469 }
470}
471
472#[cfg(target_os = "linux")]
473fn unix_reader_loop(
474 mut stream: std::os::unix::net::UnixStream,
475 id: InterfaceId,
476 tx: EventSender,
477 sleep_hold: Option<ClientSleepHold>,
478) {
479 use std::io::Read;
480 let mut decoder = hdlc::Decoder::new();
481 let mut buf = [0u8; 4096];
482
483 loop {
484 match stream.read(&mut buf) {
485 Ok(0) => {
486 let _ = tx.send(Event::InterfaceDown(id));
487 return;
488 }
489 Ok(n) => {
490 if let Some(ref sleep_hold) = sleep_hold {
491 sleep_hold.refresh();
492 }
493 for frame in decoder.feed(&buf[..n]) {
494 if tx
495 .send(Event::Frame {
496 interface_id: id,
497 data: frame,
498 rssi: None,
499 snr: None,
500 })
501 .is_err()
502 {
503 return;
504 }
505 }
506 }
507 Err(_) => {
508 let _ = tx.send(Event::InterfaceDown(id));
509 return;
510 }
511 }
512 }
513}
514
515fn spawn_local_client_handler(
517 stream: TcpStream,
518 client_id: InterfaceId,
519 tx: EventSender,
520 dynamic_template: Option<&super::DynamicInterfaceTemplate>,
521) {
522 let writer_stream = match stream.try_clone() {
523 Ok(s) => s,
524 Err(e) => {
525 log::warn!("Local server clone failed: {}", e);
526 return;
527 }
528 };
529
530 let sleep_hold = android_client_sleep_hold();
531 let info = make_local_interface_info(client_id);
532 let writer: Box<dyn Writer> = Box::new(LocalWriter {
533 stream: writer_stream,
534 sleep_hold: sleep_hold.clone(),
535 transmit_buffer: TransmitBuffer::new(),
536 });
537
538 let event = if let Some(template) = dynamic_template {
539 Event::DynamicInterfaceUp {
540 id: client_id,
541 writer,
542 registration: template.registration(info),
543 }
544 } else {
545 Event::InterfaceUp(client_id, Some(writer), Some(info))
546 };
547 if tx.send(event).is_err() {
548 return;
549 }
550
551 thread::Builder::new()
552 .name(format!("local-reader-{}", client_id.0))
553 .spawn(move || {
554 tcp_reader_loop(stream, client_id, tx, sleep_hold);
555 })
556 .ok();
557}
558
559fn tcp_reader_loop(
560 mut stream: TcpStream,
561 id: InterfaceId,
562 tx: EventSender,
563 sleep_hold: Option<ClientSleepHold>,
564) {
565 let mut decoder = hdlc::Decoder::new();
566 let mut buf = [0u8; 4096];
567
568 loop {
569 match stream.read(&mut buf) {
570 Ok(0) => {
571 log::info!("Local client {} disconnected", id.0);
572 let _ = tx.send(Event::InterfaceDown(id));
573 return;
574 }
575 Ok(n) => {
576 if let Some(ref sleep_hold) = sleep_hold {
577 sleep_hold.refresh();
578 }
579 for frame in decoder.feed(&buf[..n]) {
580 if tx
581 .send(Event::Frame {
582 interface_id: id,
583 data: frame,
584 rssi: None,
585 snr: None,
586 })
587 .is_err()
588 {
589 return;
590 }
591 }
592 }
593 Err(e) => {
594 log::warn!("Local client {} read error: {}", id.0, e);
595 let _ = tx.send(Event::InterfaceDown(id));
596 return;
597 }
598 }
599 }
600}
601
602fn make_local_interface_info(id: InterfaceId) -> InterfaceInfo {
603 InterfaceInfo {
604 id,
605 name: String::from("LocalInterface"),
606 mode: constants::MODE_FULL,
607 gravity: 0,
608 recursive_prs: false,
609 announces_from_internal: true,
610 announces_to_internal: None,
611 out_capable: true,
612 in_capable: true,
613 bitrate: Some(BITRATE),
614 airtime_profile: None,
615 announce_rate_target: None,
616 announce_rate_grace: 0,
617 announce_rate_penalty: 0.0,
618 announce_cap: constants::ANNOUNCE_CAP,
619 is_local_client: true,
620 wants_tunnel: false,
621 tunnel_id: None,
622 mtu: HW_MTU,
623 ia_freq: 0.0,
624 ip_freq: 0.0,
625 op_freq: 0.0,
626 op_samples: 0,
627 started: 0.0,
628 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
629 }
630}
631
632#[cfg(target_os = "linux")]
635enum LocalClientStream {
636 Unix(std::os::unix::net::UnixStream),
637 Tcp(TcpStream),
638}
639
640#[cfg(target_os = "linux")]
641impl LocalClientStream {
642 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
643 match self {
644 LocalClientStream::Unix(stream) => stream.read(buf),
645 LocalClientStream::Tcp(stream) => stream.read(buf),
646 }
647 }
648
649 fn writer(&self) -> io::Result<Box<dyn Writer>> {
650 match self {
651 LocalClientStream::Unix(stream) => Ok(Box::new(UnixLocalWriter {
652 stream: stream.try_clone()?,
653 sleep_hold: None,
654 transmit_buffer: TransmitBuffer::new(),
655 })),
656 LocalClientStream::Tcp(stream) => Ok(Box::new(LocalWriter {
657 stream: stream.try_clone()?,
658 sleep_hold: None,
659 transmit_buffer: TransmitBuffer::new(),
660 })),
661 }
662 }
663}
664
665#[cfg(not(target_os = "linux"))]
666type LocalClientStream = TcpStream;
667
668#[cfg(not(target_os = "linux"))]
669fn local_client_stream_writer(stream: &LocalClientStream) -> io::Result<Box<dyn Writer>> {
670 Ok(Box::new(LocalWriter {
671 stream: stream.try_clone()?,
672 sleep_hold: None,
673 transmit_buffer: TransmitBuffer::new(),
674 }))
675}
676
677#[cfg(target_os = "linux")]
678fn local_client_stream_writer(stream: &LocalClientStream) -> io::Result<Box<dyn Writer>> {
679 stream.writer()
680}
681
682fn try_connect_tcp(config: &LocalClientConfig) -> io::Result<TcpStream> {
683 let addr = format!("127.0.0.1:{}", config.port);
684 let stream = TcpStream::connect(&addr)?;
685 stream.set_nodelay(true)?;
686 log::info!(
687 "[{}] Connected to shared instance via TCP {}",
688 config.name,
689 addr
690 );
691 Ok(stream)
692}
693
694#[cfg(target_os = "linux")]
695fn try_connect_local_client(config: &LocalClientConfig) -> io::Result<LocalClientStream> {
696 match unix_socket::try_connect_unix(&config.instance_name) {
697 Ok(stream) => {
698 log::info!(
699 "[{}] Connected to shared instance via Unix socket: rns/{}",
700 config.name,
701 config.instance_name
702 );
703 Ok(LocalClientStream::Unix(stream))
704 }
705 Err(e) => {
706 log::info!(
707 "[{}] Unix socket connect failed ({}), trying TCP",
708 config.name,
709 e
710 );
711 try_connect_tcp(config).map(LocalClientStream::Tcp)
712 }
713 }
714}
715
716#[cfg(not(target_os = "linux"))]
717fn try_connect_local_client(config: &LocalClientConfig) -> io::Result<LocalClientStream> {
718 try_connect_tcp(config)
719}
720
721fn reconnect_local_client(config: &LocalClientConfig, tx: &EventSender) -> LocalClientStream {
722 loop {
723 thread::sleep(config.reconnect_wait);
724 match try_connect_local_client(config) {
725 Ok(stream) => match local_client_stream_writer(&stream) {
726 Ok(writer) => {
727 let _ = maybe_spawn_local_client_phy_keepalive(
728 &stream,
729 config.interface_id,
730 &config.name,
731 );
732 let _ = tx.send(Event::InterfaceUp(config.interface_id, Some(writer), None));
733 return stream;
734 }
735 Err(e) => {
736 log::warn!("[{}] failed to clone reconnect writer: {}", config.name, e);
737 }
738 },
739 Err(e) => {
740 log::warn!("[{}] reconnect failed: {}", config.name, e);
741 }
742 }
743 }
744}
745
746fn local_client_reader_loop(
747 mut stream: LocalClientStream,
748 config: LocalClientConfig,
749 tx: EventSender,
750) {
751 let id = config.interface_id;
752 let mut decoder = hdlc::Decoder::new();
753 let mut buf = [0u8; 4096];
754
755 loop {
756 match stream.read(&mut buf) {
757 Ok(0) => {
758 log::warn!("[{}] shared connection closed", config.name);
759 let _ = tx.send(Event::InterfaceDown(id));
760 stream = reconnect_local_client(&config, &tx);
761 decoder = hdlc::Decoder::new();
762 }
763 Ok(n) => {
764 for frame in decoder.feed(&buf[..n]) {
765 if tx
766 .send(Event::Frame {
767 interface_id: id,
768 data: frame,
769 rssi: None,
770 snr: None,
771 })
772 .is_err()
773 {
774 return;
775 }
776 }
777 }
778 Err(e) => {
779 log::warn!("[{}] shared read error: {}", config.name, e);
780 let _ = tx.send(Event::InterfaceDown(id));
781 stream = reconnect_local_client(&config, &tx);
782 decoder = hdlc::Decoder::new();
783 }
784 }
785 }
786}
787
788pub fn start_client(config: LocalClientConfig, tx: EventSender) -> io::Result<Box<dyn Writer>> {
792 let id = config.interface_id;
793 let stream = try_connect_local_client(&config)?;
794 let writer = local_client_stream_writer(&stream)?;
795 maybe_spawn_local_client_phy_keepalive(&stream, id, &config.name)?;
796
797 let _ = tx.send(Event::InterfaceUp(id, None, None));
798
799 thread::Builder::new()
800 .name(format!("local-client-reader-{}", id.0))
801 .spawn(move || {
802 local_client_reader_loop(stream, config, tx);
803 })?;
804
805 Ok(writer)
806}
807
808use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
811use std::collections::HashMap;
812
813pub struct LocalServerFactory;
815
816impl InterfaceFactory for LocalServerFactory {
817 fn type_name(&self) -> &str {
818 "LocalServerInterface"
819 }
820
821 fn parse_config(
822 &self,
823 _name: &str,
824 id: InterfaceId,
825 params: &HashMap<String, String>,
826 ) -> Result<Box<dyn InterfaceConfigData>, String> {
827 let instance_name = params
828 .get("instance_name")
829 .cloned()
830 .unwrap_or_else(|| "default".into());
831 let port = params
832 .get("port")
833 .and_then(|v| v.parse().ok())
834 .unwrap_or(37428);
835
836 Ok(Box::new(LocalServerConfig {
837 instance_name,
838 port,
839 interface_id: id,
840 }))
841 }
842
843 fn start(
844 &self,
845 config: Box<dyn InterfaceConfigData>,
846 ctx: StartContext,
847 ) -> std::io::Result<StartResult> {
848 let server_config = *config
849 .into_any()
850 .downcast::<LocalServerConfig>()
851 .map_err(|_| {
852 std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type")
853 })?;
854
855 let parent_id = server_config.interface_id;
856 let control = start_server_with_template(
857 server_config,
858 ctx.tx,
859 ctx.next_dynamic_id,
860 Some(super::DynamicInterfaceTemplate {
861 parent_id,
862 interface_type: "LocalClientInterface".into(),
863 ifac: ctx.ifac,
864 mode: ctx.mode,
865 gravity: ctx.gravity,
866 recursive_prs: ctx.recursive_prs,
867 announces_from_internal: ctx.announces_from_internal,
868 announces_to_internal: ctx.announces_to_internal,
869 }),
870 )?;
871 Ok(StartResult::Listener {
872 control: Some(control),
873 })
874 }
875}
876
877pub struct LocalClientFactory;
879
880impl InterfaceFactory for LocalClientFactory {
881 fn type_name(&self) -> &str {
882 "LocalClientInterface"
883 }
884
885 fn parse_config(
886 &self,
887 _name: &str,
888 id: InterfaceId,
889 params: &HashMap<String, String>,
890 ) -> Result<Box<dyn InterfaceConfigData>, String> {
891 let instance_name = params
892 .get("instance_name")
893 .cloned()
894 .unwrap_or_else(|| "default".into());
895 let port = params
896 .get("port")
897 .and_then(|v| v.parse().ok())
898 .unwrap_or(37428);
899
900 Ok(Box::new(LocalClientConfig {
901 instance_name,
902 port,
903 interface_id: id,
904 ..LocalClientConfig::default()
905 }))
906 }
907
908 fn start(
909 &self,
910 config: Box<dyn InterfaceConfigData>,
911 ctx: StartContext,
912 ) -> std::io::Result<StartResult> {
913 let client_config = *config
914 .into_any()
915 .downcast::<LocalClientConfig>()
916 .map_err(|_| {
917 std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong config type")
918 })?;
919
920 let id = client_config.interface_id;
921 let name = client_config.name.clone();
922 let info = InterfaceInfo {
923 id,
924 name,
925 mode: ctx.mode,
926 gravity: ctx.gravity,
927 recursive_prs: ctx.recursive_prs,
928 announces_from_internal: ctx.announces_from_internal,
929 announces_to_internal: ctx.announces_to_internal,
930 out_capable: true,
931 in_capable: true,
932 bitrate: Some(BITRATE),
933 airtime_profile: None,
934 announce_rate_target: None,
935 announce_rate_grace: 0,
936 announce_rate_penalty: 0.0,
937 announce_cap: rns_core::constants::ANNOUNCE_CAP,
938 is_local_client: false,
939 wants_tunnel: false,
940 tunnel_id: None,
941 mtu: HW_MTU,
942 ingress_control: rns_core::transport::types::IngressControlConfig::disabled(),
943 ia_freq: 0.0,
944 ip_freq: 0.0,
945 op_freq: 0.0,
946 op_samples: 0,
947 started: crate::time::now(),
948 };
949
950 let writer = start_client(client_config, ctx.tx)?;
954
955 Ok(StartResult::Simple {
956 control: None,
957 id,
958 info,
959 writer,
960 interface_type_name: "LocalInterface".to_string(),
961 })
962 }
963}
964
965#[cfg(test)]
966mod tests {
967 use super::*;
968 use std::sync::mpsc;
969 use std::sync::mpsc::RecvTimeoutError;
970
971 #[cfg(target_os = "linux")]
972 type TestClient = std::os::unix::net::UnixStream;
973
974 #[cfg(not(target_os = "linux"))]
975 type TestClient = TcpStream;
976
977 fn connect_test_client(instance_name: &str, _port: u16) -> TestClient {
978 #[cfg(target_os = "linux")]
979 {
980 unix_socket::try_connect_unix(instance_name).unwrap()
981 }
982
983 #[cfg(not(target_os = "linux"))]
984 {
985 TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap()
986 }
987 }
988
989 fn find_free_port() -> u16 {
990 crate::test_support::port()
991 }
992
993 #[test]
994 fn local_interface_metadata_uses_explicit_hardware_mtu() {
995 let info = make_local_interface_info(InterfaceId(42));
996 assert_eq!(info.bitrate, Some(1_000_000_000));
997 assert_eq!(info.mtu, 262_144);
998 }
999
1000 #[test]
1001 fn server_bind_tcp() {
1002 let port = find_free_port();
1003 let instance_name = "test-bind".to_string();
1004 let (tx, _rx) = crate::event::channel();
1005 let next_id = Arc::new(AtomicU64::new(7000));
1006
1007 let config = LocalServerConfig {
1008 instance_name: instance_name.clone(),
1009 port,
1010 interface_id: InterfaceId(70),
1011 };
1012
1013 start_server(config, tx, next_id).unwrap();
1016 thread::sleep(Duration::from_millis(50));
1017
1018 connect_test_client(&instance_name, port);
1019 }
1020
1021 #[test]
1022 fn server_accept_client() {
1023 let port = find_free_port();
1024 let instance_name = "test-accept".to_string();
1025 let (tx, rx) = crate::event::channel();
1026 let next_id = Arc::new(AtomicU64::new(7100));
1027
1028 let config = LocalServerConfig {
1029 instance_name: instance_name.clone(),
1030 port,
1031 interface_id: InterfaceId(71),
1032 };
1033
1034 start_server(config, tx, next_id).unwrap();
1035 thread::sleep(Duration::from_millis(50));
1036
1037 connect_test_client(&instance_name, port);
1038
1039 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1040 match event {
1041 Event::InterfaceUp(id, writer, info) => {
1042 assert_eq!(id, InterfaceId(7100));
1043 assert!(writer.is_some());
1044 assert!(info.is_some());
1045 }
1046 other => panic!("expected InterfaceUp, got {:?}", other),
1047 }
1048 }
1049
1050 #[test]
1051 fn server_stop_prevents_new_accepts() {
1052 let port = find_free_port();
1053 let instance_name = "test-stop".to_string();
1054 let (tx, rx) = crate::event::channel();
1055 let next_id = Arc::new(AtomicU64::new(7150));
1056
1057 let config = LocalServerConfig {
1058 instance_name: instance_name.clone(),
1059 port,
1060 interface_id: InterfaceId(71),
1061 };
1062
1063 let control = start_server(config, tx, next_id).unwrap();
1064 thread::sleep(Duration::from_millis(50));
1065 control.request_stop();
1066 thread::sleep(Duration::from_millis(120));
1067
1068 #[cfg(target_os = "linux")]
1069 let connect_result = unix_socket::try_connect_unix(&instance_name);
1070
1071 #[cfg(not(target_os = "linux"))]
1072 let connect_result = TcpStream::connect(format!("127.0.0.1:{}", port));
1073
1074 if let Ok(stream) = connect_result {
1075 drop(stream);
1076 }
1077
1078 match rx.recv_timeout(Duration::from_millis(200)) {
1079 Err(RecvTimeoutError::Timeout) | Err(RecvTimeoutError::Disconnected) => {}
1080 other => panic!("expected no InterfaceUp after server stop, got {:?}", other),
1081 }
1082 }
1083
1084 #[test]
1085 fn client_send_receive() {
1086 let port = find_free_port();
1087 let (server_tx, server_rx) = crate::event::channel();
1088 let next_id = Arc::new(AtomicU64::new(7200));
1089
1090 let server_config = LocalServerConfig {
1091 instance_name: "test-sr".into(),
1092 port,
1093 interface_id: InterfaceId(72),
1094 };
1095
1096 start_server(server_config, server_tx, next_id).unwrap();
1097 thread::sleep(Duration::from_millis(50));
1098
1099 let (client_tx, client_rx) = crate::event::channel();
1101 let client_config = LocalClientConfig {
1102 name: "test-client".into(),
1103 instance_name: "test-sr".into(),
1104 port,
1105 interface_id: InterfaceId(73),
1106 reconnect_wait: Duration::from_secs(1),
1107 };
1108
1109 let mut client_writer = start_client(client_config, client_tx).unwrap();
1110
1111 let event = server_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1113 let mut server_writer = match event {
1114 Event::InterfaceUp(_, Some(w), Some(info)) => {
1115 assert!(info.is_local_client);
1116 w
1117 }
1118 other => panic!("expected InterfaceUp with writer, got {:?}", other),
1119 };
1120
1121 let event = client_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1123 match event {
1124 Event::InterfaceUp(id, _, _) => assert_eq!(id, InterfaceId(73)),
1125 other => panic!("expected InterfaceUp, got {:?}", other),
1126 }
1127
1128 let payload: Vec<u8> = (0..32).collect();
1130 client_writer.send_frame(&payload).unwrap();
1131
1132 let event = server_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1133 match event {
1134 Event::Frame { data, .. } => assert_eq!(data, payload),
1135 other => panic!("expected Frame, got {:?}", other),
1136 }
1137
1138 let payload2: Vec<u8> = (100..132).collect();
1140 server_writer.send_frame(&payload2).unwrap();
1141
1142 let event = client_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1143 match event {
1144 Event::Frame { data, .. } => assert_eq!(data, payload2),
1145 other => panic!("expected Frame, got {:?}", other),
1146 }
1147 }
1148
1149 #[test]
1150 fn local_shared_instance_writer_is_exempt_from_egress_control() {
1151 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
1152 let client = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
1153 let (server_stream, _) = listener.accept().unwrap();
1154 let writer = LocalWriter {
1155 stream: server_stream,
1156 sleep_hold: None,
1157 transmit_buffer: TransmitBuffer::new(),
1158 };
1159
1160 assert!(writer.egress_control().is_none());
1161 drop(client);
1162 }
1163
1164 #[test]
1165 fn sleep_hold_drops_outbound_after_timeout_and_refresh_restores() {
1166 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
1167 let addr = listener.local_addr().unwrap();
1168 let mut client = TcpStream::connect(addr).unwrap();
1169 let (server_stream, _) = listener.accept().unwrap();
1170 client
1171 .set_read_timeout(Some(Duration::from_millis(200)))
1172 .unwrap();
1173
1174 let sleep_hold = ClientSleepHold::new(Duration::from_millis(30));
1175 let mut writer = LocalWriter {
1176 stream: server_stream,
1177 sleep_hold: Some(sleep_hold.clone()),
1178 transmit_buffer: TransmitBuffer::new(),
1179 };
1180
1181 writer.send_frame(b"live").unwrap();
1182 let mut buf = [0u8; 16];
1183 let n = client.read(&mut buf).unwrap();
1184 assert!(n > 0);
1185
1186 thread::sleep(Duration::from_millis(50));
1187 assert_eq!(
1188 writer
1189 .send_frame_confirmed(b"confirmed")
1190 .unwrap_err()
1191 .kind(),
1192 io::ErrorKind::PermissionDenied
1193 );
1194 writer.send_frame(b"drop").unwrap();
1195 let err = client.read(&mut buf).unwrap_err();
1196 assert!(
1197 matches!(
1198 err.kind(),
1199 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
1200 ),
1201 "paused writer should not emit bytes, got {err:?}"
1202 );
1203
1204 sleep_hold.refresh();
1205 writer.send_frame(b"again").unwrap();
1206 let n = client.read(&mut buf).unwrap();
1207 assert!(n > 0);
1208 }
1209
1210 #[test]
1211 fn tcp_reader_refreshes_sleep_hold_on_inbound_data() {
1212 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
1213 let addr = listener.local_addr().unwrap();
1214 let mut client = TcpStream::connect(addr).unwrap();
1215 let (server_stream, _) = listener.accept().unwrap();
1216 let writer_stream = server_stream.try_clone().unwrap();
1217 client
1218 .set_read_timeout(Some(Duration::from_millis(200)))
1219 .unwrap();
1220
1221 let sleep_hold = ClientSleepHold::new(Duration::from_millis(30));
1222 let reader_sleep_hold = sleep_hold.clone();
1223 let (tx, rx) = crate::event::channel();
1224 thread::spawn(move || {
1225 tcp_reader_loop(server_stream, InterfaceId(98), tx, Some(reader_sleep_hold));
1226 });
1227
1228 let mut writer = LocalWriter {
1229 stream: writer_stream,
1230 sleep_hold: Some(sleep_hold),
1231 transmit_buffer: TransmitBuffer::new(),
1232 };
1233
1234 thread::sleep(Duration::from_millis(50));
1235 writer.send_frame(b"drop").unwrap();
1236 let mut buf = [0u8; 32];
1237 let err = client.read(&mut buf).unwrap_err();
1238 assert!(
1239 matches!(
1240 err.kind(),
1241 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
1242 ),
1243 "paused writer should not emit bytes, got {err:?}"
1244 );
1245
1246 let inbound = vec![0x42; constants::HEADER_MINSIZE];
1247 client.write_all(&hdlc::frame(&inbound)).unwrap();
1248 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1249 match event {
1250 Event::Frame {
1251 interface_id,
1252 data,
1253 rssi: _,
1254 snr: _,
1255 } => {
1256 assert_eq!(interface_id, InterfaceId(98));
1257 assert_eq!(data, inbound);
1258 }
1259 other => panic!("expected Frame, got {:?}", other),
1260 }
1261
1262 writer.send_frame(b"again").unwrap();
1263 let n = client.read(&mut buf).unwrap();
1264 assert!(n > 0);
1265 }
1266
1267 #[test]
1268 fn physical_keepalive_loop_sends_empty_hdlc_frame() {
1269 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
1270 let addr = listener.local_addr().unwrap();
1271 let mut client = TcpStream::connect(addr).unwrap();
1272 let (server_stream, _) = listener.accept().unwrap();
1273 client
1274 .set_read_timeout(Some(Duration::from_millis(500)))
1275 .unwrap();
1276
1277 let writer: Box<dyn Writer> = Box::new(LocalWriter {
1278 stream: server_stream,
1279 sleep_hold: None,
1280 transmit_buffer: TransmitBuffer::new(),
1281 });
1282 spawn_physical_keepalive_loop(
1283 writer,
1284 InterfaceId(99),
1285 "test-local".into(),
1286 Duration::from_millis(10),
1287 );
1288
1289 let mut buf = [0u8; 2];
1290 client.read_exact(&mut buf).unwrap();
1291 assert_eq!(buf, [0x7E, 0x7E]);
1292 }
1293
1294 #[test]
1295 fn multiple_local_clients() {
1296 let port = find_free_port();
1297 let instance_name = "test-multi".to_string();
1298 let (tx, rx) = crate::event::channel();
1299 let next_id = Arc::new(AtomicU64::new(7300));
1300
1301 let config = LocalServerConfig {
1302 instance_name: instance_name.clone(),
1303 port,
1304 interface_id: InterfaceId(74),
1305 };
1306
1307 start_server(config, tx, next_id).unwrap();
1308 thread::sleep(Duration::from_millis(50));
1309
1310 let _client1 = connect_test_client(&instance_name, port);
1311 let _client2 = connect_test_client(&instance_name, port);
1312
1313 let mut ids = Vec::new();
1314 for _ in 0..2 {
1315 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1316 match event {
1317 Event::InterfaceUp(id, _, _) => ids.push(id),
1318 other => panic!("expected InterfaceUp, got {:?}", other),
1319 }
1320 }
1321
1322 assert_eq!(ids.len(), 2);
1323 assert_ne!(ids[0], ids[1]);
1324 }
1325
1326 #[test]
1327 fn client_disconnect_detected() {
1328 let port = find_free_port();
1329 let instance_name = "test-dc".to_string();
1330 let (tx, rx) = crate::event::channel();
1331 let next_id = Arc::new(AtomicU64::new(7400));
1332
1333 let config = LocalServerConfig {
1334 instance_name: instance_name.clone(),
1335 port,
1336 interface_id: InterfaceId(75),
1337 };
1338
1339 start_server(config, tx, next_id).unwrap();
1340 thread::sleep(Duration::from_millis(50));
1341
1342 #[cfg(target_os = "linux")]
1343 let client = unix_socket::try_connect_unix(&instance_name).unwrap();
1344
1345 #[cfg(not(target_os = "linux"))]
1346 let client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1347
1348 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
1350
1351 drop(client);
1353
1354 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1355 assert!(
1356 matches!(event, Event::InterfaceDown(_)),
1357 "expected InterfaceDown, got {:?}",
1358 event
1359 );
1360 }
1361
1362 #[test]
1363 fn client_reconnects_after_tcp_restart() {
1364 let port = find_free_port();
1365 let addr = format!("127.0.0.1:{}", port);
1366 let instance_name = format!("test-reconnect-{}", port);
1367
1368 let listener1 = TcpListener::bind(&addr).unwrap();
1369 let (accepted1_tx, accepted1_rx) = mpsc::channel();
1370 thread::spawn(move || {
1371 let (stream, _) = listener1.accept().unwrap();
1372 accepted1_tx.send(stream).unwrap();
1373 });
1374
1375 let (client_tx, client_rx) = crate::event::channel();
1376 let client_config = LocalClientConfig {
1377 name: "test-client".into(),
1378 instance_name,
1379 port,
1380 interface_id: InterfaceId(76),
1381 reconnect_wait: Duration::from_millis(50),
1382 };
1383
1384 let _writer = start_client(client_config, client_tx).unwrap();
1385 let event = client_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1386 assert!(matches!(
1387 event,
1388 Event::InterfaceUp(InterfaceId(76), None, None)
1389 ));
1390
1391 let stream1 = accepted1_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1392 drop(stream1);
1393
1394 let event = client_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1395 assert!(matches!(event, Event::InterfaceDown(InterfaceId(76))));
1396
1397 let listener2 = TcpListener::bind(&addr).unwrap();
1398 let (accepted2_tx, accepted2_rx) = mpsc::channel();
1399 thread::spawn(move || {
1400 let (stream, _) = listener2.accept().unwrap();
1401 accepted2_tx.send(stream).unwrap();
1402 });
1403
1404 let mut reconnected_writer = None;
1405 for _ in 0..10 {
1406 let event = client_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1407 match event {
1408 Event::InterfaceUp(InterfaceId(76), writer, None) if writer.is_some() => {
1409 reconnected_writer = writer;
1410 break;
1411 }
1412 _ => {}
1413 }
1414 }
1415
1416 let mut reconnected_writer = reconnected_writer.expect("missing reconnect writer");
1417 let mut stream2 = accepted2_rx.recv_timeout(Duration::from_secs(2)).unwrap();
1418 reconnected_writer.send_frame(b"client->server").unwrap();
1419 stream2
1420 .set_read_timeout(Some(Duration::from_secs(2)))
1421 .unwrap();
1422 let mut buf = [0u8; 64];
1423 let n = stream2.read(&mut buf).unwrap();
1424 assert!(n > 0, "expected bytes from refreshed writer");
1425 }
1426
1427 #[cfg(target_os = "linux")]
1428 #[test]
1429 fn unix_abstract_socket_helpers_work() {
1430 let instance_name = format!(
1431 "test-abstract-{}",
1432 std::time::SystemTime::now()
1433 .duration_since(std::time::UNIX_EPOCH)
1434 .unwrap()
1435 .as_nanos()
1436 );
1437
1438 let listener = unix_socket::try_bind_unix(&instance_name).unwrap();
1439 let accept_thread = thread::spawn(move || listener.accept().unwrap().0);
1440
1441 let mut client = unix_socket::try_connect_unix(&instance_name).unwrap();
1442 let mut server = accept_thread.join().unwrap();
1443
1444 client.write_all(b"ping").unwrap();
1445 let mut buf = [0u8; 4];
1446 server.read_exact(&mut buf).unwrap();
1447 assert_eq!(&buf, b"ping");
1448 }
1449}