1use std::io::{self, Write};
6use std::net::{TcpStream, ToSocketAddrs};
7use std::os::unix::io::AsRawFd;
8use std::sync::{Arc, Mutex};
9use std::thread;
10use std::time::Duration;
11
12use rns_core::transport::types::InterfaceId;
13
14use crate::event::{Event, EventSender, LoopContext, LoopSource, SourceStatus};
15use crate::hdlc;
16use crate::interface::{lock_or_recover, wait_while_active, ListenerControl, Writer};
17
18const HW_MTU: usize = 262_144;
20
21#[derive(Debug, Clone)]
23pub struct TcpClientConfig {
24 pub name: String,
25 pub target_host: String,
26 pub target_port: u16,
27 pub interface_id: InterfaceId,
28 pub reconnect_wait: Duration,
29 pub max_reconnect_tries: Option<u32>,
30 pub connect_timeout: Duration,
31 pub device: Option<String>,
33 pub bitrate: Option<u64>,
35 pub fixed_mtu: Option<u32>,
37 pub runtime: Arc<Mutex<TcpClientRuntime>>,
38}
39
40#[derive(Debug, Clone)]
41pub struct TcpClientRuntime {
42 pub target_host: String,
43 pub target_port: u16,
44 pub reconnect_wait: Duration,
45 pub max_reconnect_tries: Option<u32>,
46 pub connect_timeout: Duration,
47}
48
49impl TcpClientRuntime {
50 pub fn from_config(config: &TcpClientConfig) -> Self {
51 Self {
52 target_host: config.target_host.clone(),
53 target_port: config.target_port,
54 reconnect_wait: config.reconnect_wait,
55 max_reconnect_tries: config.max_reconnect_tries,
56 connect_timeout: config.connect_timeout,
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
62pub struct TcpClientRuntimeConfigHandle {
63 pub interface_name: String,
64 pub runtime: Arc<Mutex<TcpClientRuntime>>,
65 pub startup: TcpClientRuntime,
66}
67
68impl Default for TcpClientConfig {
69 fn default() -> Self {
70 let mut config = TcpClientConfig {
71 name: String::new(),
72 target_host: "127.0.0.1".into(),
73 target_port: 4242,
74 interface_id: InterfaceId(0),
75 reconnect_wait: Duration::from_secs(5),
76 max_reconnect_tries: None,
77 connect_timeout: Duration::from_secs(5),
78 device: None,
79 bitrate: None,
80 fixed_mtu: None,
81 runtime: Arc::new(Mutex::new(TcpClientRuntime {
82 target_host: "127.0.0.1".into(),
83 target_port: 4242,
84 reconnect_wait: Duration::from_secs(5),
85 max_reconnect_tries: None,
86 connect_timeout: Duration::from_secs(5),
87 })),
88 };
89 let startup = TcpClientRuntime::from_config(&config);
90 config.runtime = Arc::new(Mutex::new(startup));
91 config
92 }
93}
94
95struct TcpWriter {
97 stream: TcpStream,
98 scratch: Vec<u8>,
100}
101
102impl Writer for TcpWriter {
103 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
104 self.stream.write_all(&hdlc::frame(data))
105 }
106
107 fn send_frames(&mut self, frames: &[crate::interface::FrameBytes]) -> io::Result<()> {
108 hdlc::write_frames(&mut self.stream, frames, &mut self.scratch)
109 }
110
111 fn loop_socket(&self) -> Option<std::os::fd::RawFd> {
112 Some(self.stream.as_raw_fd())
113 }
114}
115
116pub(crate) fn set_socket_options(stream: &TcpStream) -> io::Result<()> {
120 let fd = stream.as_raw_fd();
121 unsafe {
122 let val: libc::c_int = 1;
124 if libc::setsockopt(
125 fd,
126 libc::IPPROTO_TCP,
127 libc::TCP_NODELAY,
128 &val as *const _ as *const libc::c_void,
129 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
130 ) != 0
131 {
132 return Err(io::Error::last_os_error());
133 }
134
135 if libc::setsockopt(
137 fd,
138 libc::SOL_SOCKET,
139 libc::SO_KEEPALIVE,
140 &val as *const _ as *const libc::c_void,
141 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
142 ) != 0
143 {
144 return Err(io::Error::last_os_error());
145 }
146
147 #[cfg(target_os = "linux")]
149 {
150 let idle: libc::c_int = 5;
152 if libc::setsockopt(
153 fd,
154 libc::IPPROTO_TCP,
155 libc::TCP_KEEPIDLE,
156 &idle as *const _ as *const libc::c_void,
157 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
158 ) != 0
159 {
160 return Err(io::Error::last_os_error());
161 }
162
163 let intvl: libc::c_int = 2;
165 if libc::setsockopt(
166 fd,
167 libc::IPPROTO_TCP,
168 libc::TCP_KEEPINTVL,
169 &intvl as *const _ as *const libc::c_void,
170 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
171 ) != 0
172 {
173 return Err(io::Error::last_os_error());
174 }
175
176 let cnt: libc::c_int = 12;
178 if libc::setsockopt(
179 fd,
180 libc::IPPROTO_TCP,
181 libc::TCP_KEEPCNT,
182 &cnt as *const _ as *const libc::c_void,
183 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
184 ) != 0
185 {
186 return Err(io::Error::last_os_error());
187 }
188
189 let timeout: libc::c_int = 24_000;
191 if libc::setsockopt(
192 fd,
193 libc::IPPROTO_TCP,
194 libc::TCP_USER_TIMEOUT,
195 &timeout as *const _ as *const libc::c_void,
196 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
197 ) != 0
198 {
199 return Err(io::Error::last_os_error());
200 }
201 }
202 }
203 Ok(())
204}
205
206fn try_connect(config: &TcpClientConfig, underlay_mark: Option<u32>) -> io::Result<TcpStream> {
208 let runtime = lock_or_recover(&config.runtime, "tcp client runtime").clone();
209 let addr_str = format!("{}:{}", config.target_host, config.target_port);
210 let addr = addr_str
211 .to_socket_addrs()?
212 .next()
213 .ok_or_else(|| io::Error::new(io::ErrorKind::AddrNotAvailable, "no addresses resolved"))?;
214
215 #[cfg(target_os = "linux")]
216 let stream = if config.device.is_some() || underlay_mark.is_some() {
217 super::connect_tcp_with_options(
218 &addr,
219 config.device.as_deref(),
220 underlay_mark,
221 runtime.connect_timeout,
222 )?
223 } else {
224 TcpStream::connect_timeout(&addr, runtime.connect_timeout)?
225 };
226 #[cfg(not(target_os = "linux"))]
227 let stream = TcpStream::connect_timeout(&addr, runtime.connect_timeout)?;
228 set_socket_options(&stream)?;
229 Ok(stream)
230}
231
232pub fn start(config: TcpClientConfig, tx: EventSender) -> io::Result<Box<dyn Writer>> {
234 start_with_ifac(config, tx, 0, None)
235}
236
237fn start_with_ifac(
238 config: TcpClientConfig,
239 tx: EventSender,
240 ifac_size: usize,
241 underlay_mark: Option<u32>,
242) -> io::Result<Box<dyn Writer>> {
243 start_with_ifac_control(config, tx, ifac_size, underlay_mark).map(|(writer, _)| writer)
244}
245
246fn start_with_ifac_control(
247 config: TcpClientConfig,
248 tx: EventSender,
249 ifac_size: usize,
250 underlay_mark: Option<u32>,
251) -> io::Result<(Box<dyn Writer>, ListenerControl)> {
252 let stream = try_connect(&config, underlay_mark)?;
253 let writer_stream = stream.try_clone()?;
254
255 let id = config.interface_id;
256 let _ = tx.send(Event::InterfaceUp(id, None, None));
258
259 let control = ListenerControl::new();
260 let link = Arc::new(ClientLink {
261 config,
262 tx,
263 ifac_size,
264 underlay_mark,
265 control: control.clone(),
266 });
267 if let Err(error) = link.serve(stream) {
268 let _ = link.tx.send(Event::InterfaceDown(id));
269 return Err(error);
270 }
271
272 Ok((
273 Box::new(TcpWriter {
274 stream: writer_stream,
275 scratch: Vec::new(),
276 }),
277 control,
278 ))
279}
280
281pub(crate) const STREAM_READ_BUDGET: usize = 256 * 1024;
284
285pub(crate) enum StreamRead {
287 Rearm,
289 Suspend,
292 Closed,
294 Failed(io::Error),
295 DriverGone,
297}
298
299pub(crate) fn read_stream_frames(
305 fd: std::os::fd::RawFd,
306 decoder: &mut hdlc::Decoder,
307 cx: &mut LoopContext<'_>,
308 tx: &EventSender,
309 id: InterfaceId,
310 name: &str,
311 mut on_read: impl FnMut(usize, usize),
312) -> StreamRead {
313 if !tx.inbound_data_has_room() {
314 return StreamRead::Suspend;
315 }
316 let mut read = 0;
317 while read < STREAM_READ_BUDGET {
318 let n = unsafe {
321 libc::recv(
322 fd,
323 cx.buf.as_mut_ptr().cast(),
324 cx.buf.len(),
325 libc::MSG_DONTWAIT,
326 )
327 };
328 if n < 0 {
329 let e = io::Error::last_os_error();
330 match e.kind() {
331 io::ErrorKind::WouldBlock => return StreamRead::Rearm,
332 io::ErrorKind::Interrupted => continue,
333 _ => return StreamRead::Failed(e),
334 }
335 }
336 if n == 0 {
337 return StreamRead::Closed;
338 }
339 let n = n as usize;
340 read += n;
341 let decoded = decoder.feed_with_diagnostics(&cx.buf[..n]);
342 for frame_len in decoded.invalid_frame_lengths {
343 log::debug!(
344 "[{}] invalid HDLC frame of {} bytes received on interface {}, dropping frame",
345 name,
346 frame_len,
347 id.0,
348 );
349 }
350 on_read(n, decoded.frames.len());
351 let frames = decoded.frames.into_iter().map(|frame| Event::Frame {
352 interface_id: id,
353 data: frame,
354 rssi: None,
355 snr: None,
356 });
357 match tx.push_from_loop(frames) {
358 Ok(true) => {}
359 Ok(false) => return StreamRead::Suspend,
360 Err(_) => return StreamRead::DriverGone,
361 }
362 }
363 StreamRead::Rearm
364}
365
366struct ClientLink {
368 config: TcpClientConfig,
369 tx: EventSender,
370 ifac_size: usize,
371 underlay_mark: Option<u32>,
372 control: ListenerControl,
373}
374
375impl ClientLink {
376 fn serve(self: &Arc<Self>, stream: TcpStream) -> io::Result<()> {
378 let source = ReaderSource {
379 stream,
380 decoder: hdlc::Decoder::reticulum(HW_MTU, self.ifac_size),
381 link: Arc::clone(self),
382 };
383 self.tx.register_source(
384 crate::interface::loop_writer::read_key(self.config.interface_id),
385 Box::new(source),
386 )
387 }
388
389 fn connection_lost(self: &Arc<Self>) {
393 let id = self.config.interface_id;
394 let _ = self.tx.push_from_loop([Event::InterfaceDown(id)]);
395 let link = Arc::clone(self);
396 let spawned = thread::Builder::new()
397 .name(format!("tcp-reconn-{}", id.0))
398 .spawn(move || link.reconnect_loop());
399 if let Err(error) = spawned {
400 log::error!(
401 "[{}] cannot start reconnecting, giving up: {}",
402 self.config.name,
403 error
404 );
405 }
406 }
407
408 fn reconnect_loop(self: Arc<Self>) {
409 loop {
410 let Some(stream) = reconnect(&self.config, &self.tx, self.underlay_mark, &self.control)
411 else {
412 if !self.control.should_stop() {
413 log::error!("[{}] reconnection failed, giving up", self.config.name);
414 }
415 return;
416 };
417 match self.serve(stream) {
418 Ok(()) => return,
419 Err(error) => {
420 log::warn!(
421 "[{}] cannot read the new connection: {}",
422 self.config.name,
423 error
424 );
425 let _ = self.tx.send(Event::InterfaceDown(self.config.interface_id));
426 }
427 }
428 }
429 }
430}
431
432struct ReaderSource {
434 stream: TcpStream,
435 decoder: hdlc::Decoder,
436 link: Arc<ClientLink>,
437}
438
439impl LoopSource for ReaderSource {
440 fn fd(&self) -> std::os::fd::RawFd {
441 self.stream.as_raw_fd()
442 }
443
444 fn stopped(&self) -> bool {
445 self.link.control.should_stop()
446 }
447
448 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
449 let link = &self.link;
450 if link.control.should_stop() {
451 return SourceStatus::Remove;
452 }
453 let name = &link.config.name;
454 match read_stream_frames(
455 self.stream.as_raw_fd(),
456 &mut self.decoder,
457 cx,
458 &link.tx,
459 link.config.interface_id,
460 name,
461 |_, _| {},
462 ) {
463 StreamRead::Rearm => SourceStatus::Rearm,
464 StreamRead::Suspend => SourceStatus::Suspend,
465 StreamRead::Closed => {
466 log::warn!("[{}] connection closed", name);
467 link.connection_lost();
468 SourceStatus::Remove
469 }
470 StreamRead::Failed(error) => {
471 log::warn!("[{}] read error: {}", name, error);
472 link.connection_lost();
473 SourceStatus::Remove
474 }
475 StreamRead::DriverGone => SourceStatus::Remove,
476 }
477 }
478}
479
480fn reconnect(
483 config: &TcpClientConfig,
484 tx: &EventSender,
485 underlay_mark: Option<u32>,
486 control: &ListenerControl,
487) -> Option<TcpStream> {
488 let mut attempts = 0u32;
489 loop {
490 let runtime = lock_or_recover(&config.runtime, "tcp client runtime").clone();
491 if !wait_while_active(control, runtime.reconnect_wait) {
492 return None;
493 }
494 attempts += 1;
495
496 if let Some(max) = runtime.max_reconnect_tries {
497 if attempts > max {
498 let _ = tx.send(Event::InterfaceDown(config.interface_id));
499 return None;
500 }
501 }
502
503 log::info!("[{}] reconnect attempt {} ...", config.name, attempts);
504
505 match try_connect(config, underlay_mark) {
506 Ok(new_stream) => {
507 if control.should_stop() {
508 return None;
509 }
510 let writer_stream = match new_stream.try_clone() {
512 Ok(s) => s,
513 Err(e) => {
514 log::warn!("[{}] failed to clone stream: {}", config.name, e);
515 continue;
516 }
517 };
518 log::info!("[{}] reconnected", config.name);
519 let new_writer: Box<dyn Writer> = Box::new(TcpWriter {
521 stream: writer_stream,
522 scratch: Vec::new(),
523 });
524 let _ = tx.send(Event::InterfaceUp(
525 config.interface_id,
526 Some(new_writer),
527 None,
528 ));
529 return Some(new_stream);
530 }
531 Err(e) => {
532 log::warn!("[{}] reconnect failed: {}", config.name, e);
533 }
534 }
535 }
536}
537
538use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
541use rns_core::transport::types::InterfaceInfo;
542use std::collections::HashMap;
543
544pub struct TcpClientFactory;
546
547impl InterfaceFactory for TcpClientFactory {
548 fn type_name(&self) -> &str {
549 "TCPClientInterface"
550 }
551
552 fn parse_config(
553 &self,
554 name: &str,
555 id: InterfaceId,
556 params: &HashMap<String, String>,
557 ) -> Result<Box<dyn InterfaceConfigData>, String> {
558 let target_host = params
559 .get("target_host")
560 .cloned()
561 .unwrap_or_else(|| "127.0.0.1".into());
562 let target_port = params
563 .get("target_port")
564 .and_then(|v| v.parse().ok())
565 .unwrap_or(4242);
566 let (bitrate, fixed_mtu) = super::parse_tcp_rate_params(params)?;
567
568 Ok(Box::new(TcpClientConfig {
569 name: name.to_string(),
570 target_host,
571 target_port,
572 interface_id: id,
573 device: params.get("device").cloned(),
574 bitrate,
575 fixed_mtu,
576 ..TcpClientConfig::default()
577 }))
578 }
579
580 fn start(
581 &self,
582 config: Box<dyn InterfaceConfigData>,
583 ctx: StartContext,
584 ) -> io::Result<StartResult> {
585 let tcp_config = *config
586 .into_any()
587 .downcast::<TcpClientConfig>()
588 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong config type"))?;
589
590 let id = tcp_config.interface_id;
591 let name = tcp_config.name.clone();
592 let info = InterfaceInfo {
593 id,
594 name,
595 mode: ctx.mode,
596 gravity: ctx.gravity,
597 recursive_prs: ctx.recursive_prs,
598 announces_from_internal: ctx.announces_from_internal,
599 announces_to_internal: ctx.announces_to_internal,
600 out_capable: true,
601 in_capable: true,
602 bitrate: tcp_config.bitrate,
603 airtime_profile: None,
604 announce_rate_target: None,
605 announce_rate_grace: 0,
606 announce_rate_penalty: 0.0,
607 announce_cap: rns_core::constants::ANNOUNCE_CAP,
608 is_local_client: false,
609 wants_tunnel: false,
610 tunnel_id: None,
611 mtu: super::tcp_hardware_mtu(tcp_config.bitrate, tcp_config.fixed_mtu),
612 ingress_control: rns_core::transport::types::IngressControlConfig::enabled(),
613 ia_freq: 0.0,
614 ip_freq: 0.0,
615 op_freq: 0.0,
616 op_samples: 0,
617 started: crate::time::now(),
618 };
619
620 let ifac_size = ctx.ifac.as_ref().map(|ifac| ifac.size).unwrap_or(0);
621 let (writer, control) =
622 start_with_ifac_control(tcp_config, ctx.tx, ifac_size, ctx.underlay_mark)?;
623
624 Ok(StartResult::Simple {
625 control: Some(control),
626 id,
627 info,
628 writer,
629 interface_type_name: "TCPClientInterface".to_string(),
630 })
631 }
632}
633
634pub(crate) fn tcp_client_runtime_handle_from_config(
635 config: &TcpClientConfig,
636) -> TcpClientRuntimeConfigHandle {
637 TcpClientRuntimeConfigHandle {
638 interface_name: config.name.clone(),
639 runtime: Arc::clone(&config.runtime),
640 startup: TcpClientRuntime::from_config(config),
641 }
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647 use std::io::Read;
648
649 #[test]
650 fn detach_stops_idle_tcp_reader_without_reconnect() {
651 let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
652 let mut client = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
653 let (server, _) = listener.accept().unwrap();
654 let (tx, rx) = crate::event::channel();
655 let control = ListenerControl::new();
656 let link = Arc::new(ClientLink {
657 config: TcpClientConfig {
658 name: "detach-test".into(),
659 interface_id: InterfaceId(75),
660 ..Default::default()
661 },
662 tx,
663 ifac_size: 0,
664 underlay_mark: None,
665 control: control.clone(),
666 });
667 link.serve(server).unwrap();
668 control.request_stop();
669 rx.sweep_stopped_sources();
670
671 client
673 .set_read_timeout(Some(Duration::from_secs(2)))
674 .unwrap();
675 let mut buf = [0u8; 1];
676 assert_eq!(std::io::Read::read(&mut client, &mut buf).unwrap(), 0);
677 assert!(rx.try_recv().is_err());
678 drop(link);
679 }
680
681 #[test]
682 fn a_reconnected_client_is_read_on_the_receiving_thread() {
683 let port = find_free_port();
684 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
685 let (tx, rx) = crate::event::channel();
686 let config = TcpClientConfig {
687 interface_id: InterfaceId(76),
688 ..make_config(port)
689 };
690 let _writer = start(config, tx).unwrap();
691 let (first, _) = listener.accept().unwrap();
692 assert!(matches!(
693 rx.recv_timeout(Duration::from_secs(1)).unwrap(),
694 Event::InterfaceUp(InterfaceId(76), None, _)
695 ));
696 drop(first);
697 assert!(matches!(
698 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
699 Event::InterfaceDown(InterfaceId(76))
700 ));
701 let (mut second, _) = listener.accept().unwrap();
702 assert!(matches!(
703 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
704 Event::InterfaceUp(InterfaceId(76), Some(_), _)
705 ));
706 let payload: Vec<u8> = (0..32).collect();
707 second.write_all(&hdlc::frame(&payload)).unwrap();
708 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
709 Event::Frame { data, .. } => assert_eq!(data, payload),
710 other => panic!("expected Frame, got {:?}", other),
711 }
712 let reconnecting = || {
714 std::fs::read_dir("/proc/self/task")
715 .unwrap()
716 .filter_map(|task| std::fs::read_to_string(task.ok()?.path().join("comm")).ok())
717 .any(|name| name.trim_end() == "tcp-reconn-76")
718 };
719 let deadline = std::time::Instant::now() + Duration::from_secs(2);
720 while reconnecting() && std::time::Instant::now() < deadline {
721 thread::sleep(Duration::from_millis(10));
722 }
723 assert!(!reconnecting());
724 }
725
726 use std::net::TcpListener;
727 use std::time::Duration;
728
729 fn find_free_port() -> u16 {
730 crate::test_support::port()
731 }
732
733 fn make_config(port: u16) -> TcpClientConfig {
734 TcpClientConfig {
735 name: format!("test-tcp-{}", port),
736 target_host: "127.0.0.1".into(),
737 target_port: port,
738 interface_id: InterfaceId(1),
739 reconnect_wait: Duration::from_millis(100),
740 max_reconnect_tries: Some(2),
741 connect_timeout: Duration::from_secs(2),
742 runtime: Arc::new(Mutex::new(TcpClientRuntime {
743 target_host: "127.0.0.1".into(),
744 target_port: port,
745 reconnect_wait: Duration::from_millis(100),
746 max_reconnect_tries: Some(2),
747 connect_timeout: Duration::from_secs(2),
748 })),
749 device: None,
750 bitrate: None,
751 fixed_mtu: None,
752 }
753 }
754
755 #[test]
756 fn connect_to_listener() {
757 let port = find_free_port();
758 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
759 let (tx, rx) = crate::event::channel();
760
761 let config = make_config(port);
762 let _writer = start(config, tx).unwrap();
763
764 let _server_stream = listener.accept().unwrap();
766
767 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
769 assert!(matches!(event, Event::InterfaceUp(InterfaceId(1), _, _)));
770 }
771
772 #[test]
773 fn receive_frame() {
774 let port = find_free_port();
775 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
776 let (tx, rx) = crate::event::channel();
777
778 let config = make_config(port);
779 let _writer = start(config, tx).unwrap();
780
781 let (mut server_stream, _) = listener.accept().unwrap();
782
783 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
785
786 let payload: Vec<u8> = (0..32).collect();
788 let framed = hdlc::frame(&payload);
789 server_stream.write_all(&framed).unwrap();
790
791 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
793 match event {
794 Event::Frame {
795 interface_id,
796 data,
797 rssi: _,
798 snr: _,
799 } => {
800 assert_eq!(interface_id, InterfaceId(1));
801 assert_eq!(data, payload);
802 }
803 other => panic!("expected Frame, got {:?}", other),
804 }
805 }
806
807 #[test]
808 fn reader_enforces_upstream_frame_bounds_and_ifac_allowance() {
809 let port = find_free_port();
810 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
811 let (tx, rx) = crate::event::channel();
812 let ifac_size = 8;
813
814 let config = make_config(port);
815 let _writer = start_with_ifac(config, tx, ifac_size, None).unwrap();
816 let (mut server_stream, _) = listener.accept().unwrap();
817 assert!(matches!(
818 rx.recv_timeout(Duration::from_secs(1)).unwrap(),
819 Event::InterfaceUp(_, _, _)
820 ));
821
822 server_stream
825 .write_all(&hdlc::frame(&[0x11; rns_core::constants::HEADER_MINSIZE]))
826 .unwrap();
827 let minimum_valid = vec![0x22; rns_core::constants::HEADER_MINSIZE + 1];
828 server_stream
829 .write_all(&hdlc::frame(&minimum_valid))
830 .unwrap();
831 assert!(matches!(
832 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
833 Event::Frame { data, .. } if data == minimum_valid
834 ));
835
836 let maximum_valid = vec![0x33; HW_MTU + ifac_size];
838 server_stream
839 .write_all(&hdlc::frame(&maximum_valid))
840 .unwrap();
841 assert!(matches!(
842 rx.recv_timeout(Duration::from_secs(5)).unwrap(),
843 Event::Frame { data, .. } if data == maximum_valid
844 ));
845
846 server_stream
848 .write_all(&hdlc::frame(&vec![0x44; HW_MTU + ifac_size + 1]))
849 .unwrap();
850 let recovery = vec![0x55; rns_core::constants::HEADER_MINSIZE + 1];
851 server_stream.write_all(&hdlc::frame(&recovery)).unwrap();
852 assert!(matches!(
853 rx.recv_timeout(Duration::from_secs(5)).unwrap(),
854 Event::Frame { data, .. } if data == recovery
855 ));
856 }
857
858 #[test]
859 fn send_frame() {
860 let port = find_free_port();
861 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
862 let (tx, _rx) = crate::event::channel();
863
864 let config = make_config(port);
865 let mut writer = start(config, tx).unwrap();
866
867 let (mut server_stream, _) = listener.accept().unwrap();
868 server_stream
869 .set_read_timeout(Some(Duration::from_secs(2)))
870 .unwrap();
871
872 let payload: Vec<u8> = (0..24).collect();
874 writer.send_frame(&payload).unwrap();
875
876 let mut buf = [0u8; 256];
878 let n = server_stream.read(&mut buf).unwrap();
879 let expected = hdlc::frame(&payload);
880 assert_eq!(&buf[..n], &expected[..]);
881 }
882
883 #[test]
884 fn multiple_frames() {
885 let port = find_free_port();
886 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
887 let (tx, rx) = crate::event::channel();
888
889 let config = make_config(port);
890 let _writer = start(config, tx).unwrap();
891
892 let (mut server_stream, _) = listener.accept().unwrap();
893
894 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
896
897 let payloads: Vec<Vec<u8>> = (0..3)
899 .map(|i| (0..24).map(|j| j + i * 50).collect())
900 .collect();
901 for p in &payloads {
902 server_stream.write_all(&hdlc::frame(p)).unwrap();
903 }
904
905 for expected in &payloads {
907 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
908 match event {
909 Event::Frame { data, .. } => assert_eq!(&data, expected),
910 other => panic!("expected Frame, got {:?}", other),
911 }
912 }
913 }
914
915 #[test]
916 fn split_across_reads() {
917 let port = find_free_port();
918 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
919 let (tx, rx) = crate::event::channel();
920
921 let config = make_config(port);
922 let _writer = start(config, tx).unwrap();
923
924 let (mut server_stream, _) = listener.accept().unwrap();
925
926 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
928
929 let payload: Vec<u8> = (0..32).collect();
931 let framed = hdlc::frame(&payload);
932 let mid = framed.len() / 2;
933
934 server_stream.write_all(&framed[..mid]).unwrap();
935 server_stream.flush().unwrap();
936 thread::sleep(Duration::from_millis(50));
937 server_stream.write_all(&framed[mid..]).unwrap();
938 server_stream.flush().unwrap();
939
940 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
941 match event {
942 Event::Frame { data, .. } => assert_eq!(data, payload),
943 other => panic!("expected Frame, got {:?}", other),
944 }
945 }
946
947 #[test]
948 fn reconnect_on_close() {
949 let port = find_free_port();
950 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
951 listener.set_nonblocking(false).unwrap();
952 let (tx, rx) = crate::event::channel();
953
954 let config = make_config(port);
955 let _writer = start(config, tx).unwrap();
956
957 let (server_stream, _) = listener.accept().unwrap();
959
960 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
962
963 drop(server_stream);
964
965 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
967 assert!(matches!(event, Event::InterfaceDown(InterfaceId(1))));
968
969 let _server_stream2 = listener.accept().unwrap();
971
972 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
974 assert!(matches!(event, Event::InterfaceUp(InterfaceId(1), _, _)));
975 }
976
977 #[test]
978 fn socket_options() {
979 let port = find_free_port();
980 let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap();
981
982 let stream = try_connect(&make_config(port), None).unwrap();
983 let _server = listener.accept().unwrap();
984
985 let fd = stream.as_raw_fd();
987 let mut val: libc::c_int = 0;
988 let mut len: libc::socklen_t = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
989 unsafe {
990 libc::getsockopt(
991 fd,
992 libc::IPPROTO_TCP,
993 libc::TCP_NODELAY,
994 &mut val as *mut _ as *mut libc::c_void,
995 &mut len,
996 );
997 }
998 assert_eq!(val, 1, "TCP_NODELAY should be 1");
999 }
1000
1001 #[test]
1002 fn connect_timeout() {
1003 let config = TcpClientConfig {
1005 name: "timeout-test".into(),
1006 target_host: "192.0.2.1".into(), target_port: 12345,
1008 interface_id: InterfaceId(99),
1009 reconnect_wait: Duration::from_millis(100),
1010 max_reconnect_tries: Some(0),
1011 connect_timeout: Duration::from_millis(500),
1012 device: None,
1013 bitrate: None,
1014 fixed_mtu: None,
1015 runtime: Arc::new(Mutex::new(TcpClientRuntime {
1016 target_host: "192.0.2.1".into(),
1017 target_port: 12345,
1018 reconnect_wait: Duration::from_millis(100),
1019 max_reconnect_tries: Some(0),
1020 connect_timeout: Duration::from_millis(500),
1021 })),
1022 };
1023
1024 let start_time = std::time::Instant::now();
1025 let result = try_connect(&config, None);
1026 let elapsed = start_time.elapsed();
1027
1028 assert!(result.is_err());
1029 assert!(elapsed < Duration::from_secs(5));
1031 }
1032}