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