1use std::io::{self, Write};
8use std::net::{TcpListener, TcpStream};
9use std::os::unix::io::AsRawFd;
10use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
11use std::sync::{Arc, Mutex};
12
13use rns_core::constants;
14use rns_core::transport::types::{IngressControlConfig, InterfaceId, InterfaceInfo};
15
16use crate::event::{Event, EventSender, LoopContext, LoopSource, SourceStatus};
17use crate::hdlc;
18use crate::interface::tcp::{read_stream_frames, StreamRead};
19use crate::interface::{lock_or_recover, ListenerControl, Writer};
20
21const HW_MTU: usize = 262_144;
23
24#[derive(Debug, Clone)]
26pub struct TcpServerConfig {
27 pub name: String,
28 pub listen_ip: String,
29 pub listen_port: u16,
30 pub interface_id: InterfaceId,
31 pub max_connections: Option<usize>,
32 pub ingress_control: IngressControlConfig,
33 pub bitrate: Option<u64>,
35 pub fixed_mtu: Option<u32>,
37 pub runtime: Arc<Mutex<TcpServerRuntime>>,
38}
39
40#[derive(Debug, Clone)]
41pub struct TcpServerRuntime {
42 pub max_connections: Option<usize>,
43}
44
45impl TcpServerRuntime {
46 pub fn from_config(config: &TcpServerConfig) -> Self {
47 Self {
48 max_connections: config.max_connections,
49 }
50 }
51}
52
53#[derive(Debug, Clone)]
54pub struct TcpServerRuntimeConfigHandle {
55 pub interface_name: String,
56 pub runtime: Arc<Mutex<TcpServerRuntime>>,
57 pub startup: TcpServerRuntime,
58}
59
60impl Default for TcpServerConfig {
61 fn default() -> Self {
62 let mut config = TcpServerConfig {
63 name: String::new(),
64 listen_ip: "0.0.0.0".into(),
65 listen_port: 4242,
66 interface_id: InterfaceId(0),
67 max_connections: None,
68 ingress_control: IngressControlConfig::enabled(),
69 bitrate: None,
70 fixed_mtu: None,
71 runtime: Arc::new(Mutex::new(TcpServerRuntime {
72 max_connections: None,
73 })),
74 };
75 let startup = TcpServerRuntime::from_config(&config);
76 config.runtime = Arc::new(Mutex::new(startup));
77 config
78 }
79}
80
81struct TcpServerWriter {
83 stream: TcpStream,
84 scratch: Vec<u8>,
86}
87
88impl Writer for TcpServerWriter {
89 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
90 self.stream.write_all(&hdlc::frame(data))
91 }
92
93 fn send_frames(&mut self, frames: &[crate::interface::FrameBytes]) -> io::Result<()> {
94 hdlc::write_frames(&mut self.stream, frames, &mut self.scratch)
95 }
96
97 fn loop_socket(&self) -> Option<std::os::fd::RawFd> {
98 Some(self.stream.as_raw_fd())
99 }
100}
101
102pub fn start(
108 config: TcpServerConfig,
109 tx: EventSender,
110 next_id: Arc<AtomicU64>,
111) -> io::Result<ListenerControl> {
112 start_with_template(config, tx, next_id, None, None)
113}
114
115fn start_with_template(
116 config: TcpServerConfig,
117 tx: EventSender,
118 next_id: Arc<AtomicU64>,
119 dynamic_template: Option<super::DynamicInterfaceTemplate>,
120 underlay_mark: Option<u32>,
121) -> io::Result<ListenerControl> {
122 let addr = format!("{}:{}", config.listen_ip, config.listen_port);
123 let listener = TcpListener::bind(&addr)?;
124 super::apply_underlay_mark(listener.as_raw_fd(), underlay_mark)?;
125 listener.set_nonblocking(true)?;
126
127 log::info!("[{}] TCP server listening on {}", config.name, addr);
128
129 let name = config.name.clone();
130 let ifac_size = dynamic_template
131 .as_ref()
132 .and_then(|template| template.ifac.as_ref())
133 .map(|ifac| ifac.size)
134 .unwrap_or(0);
135 let runtime = Arc::clone(&config.runtime);
136 let ingress_control = config.ingress_control;
137 let bitrate = config.bitrate;
138 let mtu = super::tcp_hardware_mtu(config.bitrate, config.fixed_mtu);
139 let active_connections = Arc::new(AtomicUsize::new(0));
140 let control = ListenerControl::new();
141 let listener_key = crate::interface::loop_writer::read_key(config.interface_id);
142 tx.register_source(
143 listener_key,
144 Box::new(ListenerSource {
145 listener,
146 name,
147 tx: tx.clone(),
148 next_id,
149 runtime,
150 ingress_control,
151 bitrate,
152 mtu,
153 active_connections,
154 control: control.clone(),
155 dynamic_template,
156 ifac_size,
157 underlay_mark,
158 }),
159 )?;
160
161 Ok(control)
162}
163
164const ACCEPT_BURST: usize = 64;
166
167struct ListenerSource {
170 listener: TcpListener,
171 name: String,
172 tx: EventSender,
173 next_id: Arc<AtomicU64>,
174 runtime: Arc<Mutex<TcpServerRuntime>>,
175 ingress_control: IngressControlConfig,
176 bitrate: Option<u64>,
177 mtu: u32,
178 active_connections: Arc<AtomicUsize>,
179 control: ListenerControl,
180 dynamic_template: Option<super::DynamicInterfaceTemplate>,
181 ifac_size: usize,
182 underlay_mark: Option<u32>,
183}
184
185impl LoopSource for ListenerSource {
186 fn fd(&self) -> std::os::fd::RawFd {
187 self.listener.as_raw_fd()
188 }
189
190 fn stopped(&self) -> bool {
191 self.control.should_stop()
192 }
193
194 fn on_removed(&mut self) {
195 log::info!("[{}] listener stopping", self.name);
196 }
197
198 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
199 if self.control.should_stop() {
200 return SourceStatus::Remove;
201 }
202 for _ in 0..ACCEPT_BURST {
203 let stream = match self.listener.accept() {
204 Ok((stream, _)) => stream,
205 Err(e) => match accept_error_step(&e) {
206 AcceptStep::Drained => break,
207 AcceptStep::Next => {
208 log::debug!("[{}] accept failed: {}", self.name, e);
209 continue;
210 }
211 AcceptStep::Pause => {
212 log::warn!(
215 "[{}] accept failed: {}; accepting resumes on the next tick",
216 self.name,
217 e
218 );
219 return SourceStatus::Pause;
220 }
221 },
222 };
223 match self.admit(stream) {
224 Admitted::Client(id, source) => {
225 cx.add_source(crate::interface::loop_writer::read_key(id), source)
226 }
227 Admitted::Rejected => {}
228 Admitted::DriverGone => return SourceStatus::Remove,
229 }
230 }
231 SourceStatus::Rearm
232 }
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub(crate) enum AcceptStep {
238 Drained,
240 Next,
242 Pause,
245}
246
247pub(crate) fn accept_error_step(error: &io::Error) -> AcceptStep {
248 match error.kind() {
249 io::ErrorKind::WouldBlock => AcceptStep::Drained,
250 io::ErrorKind::Interrupted
251 | io::ErrorKind::ConnectionAborted
252 | io::ErrorKind::ConnectionReset
253 | io::ErrorKind::NetworkDown
254 | io::ErrorKind::NetworkUnreachable
255 | io::ErrorKind::HostUnreachable => AcceptStep::Next,
256 _ if error.raw_os_error() == Some(libc::EPROTO) => AcceptStep::Next,
259 _ => AcceptStep::Pause,
260 }
261}
262
263enum Admitted {
264 Client(InterfaceId, Box<dyn LoopSource>),
265 Rejected,
266 DriverGone,
267}
268
269impl ListenerSource {
270 fn admit(&self, stream: TcpStream) -> Admitted {
271 let name = &self.name;
272 if let Err(error) = super::apply_underlay_mark(stream.as_raw_fd(), self.underlay_mark) {
273 log::error!(
274 "[{}] failed to mark accepted underlay socket: {}",
275 name,
276 error
277 );
278 return Admitted::Rejected;
279 }
280
281 if let Err(error) = super::tcp::set_socket_options(&stream) {
284 log::warn!(
285 "[{}] failed to set socket options for {:?}: {}",
286 name,
287 stream.peer_addr().ok(),
288 error
289 );
290 }
291
292 let max_connections = lock_or_recover(&self.runtime, "tcp server runtime").max_connections;
293 if let Some(max) = max_connections {
294 if self.active_connections.load(Ordering::Relaxed) >= max {
295 let peer = stream.peer_addr().ok();
296 log::warn!(
297 "[{}] max connections ({}) reached, rejecting {:?}",
298 name,
299 max,
300 peer
301 );
302 return Admitted::Rejected;
303 }
304 }
305
306 let client_id = InterfaceId(self.next_id.fetch_add(1, Ordering::Relaxed));
307 let peer_addr = stream.peer_addr().ok();
308
309 log::info!(
310 "[{}] client connected: {:?} → id {}",
311 name,
312 peer_addr,
313 client_id.0
314 );
315
316 let writer_stream = match stream.try_clone() {
318 Ok(s) => s,
319 Err(e) => {
320 log::warn!("[{}] failed to clone stream: {}", name, e);
321 return Admitted::Rejected;
322 }
323 };
324
325 let writer: Box<dyn Writer> = Box::new(TcpServerWriter {
326 stream: writer_stream,
327 scratch: Vec::new(),
328 });
329
330 let info = InterfaceInfo {
331 id: client_id,
332 name: format!("TCPServerInterface/Client-{}", client_id.0),
333 mode: constants::MODE_FULL,
334 gravity: 0,
335 recursive_prs: false,
336 announces_from_internal: true,
337 announces_to_internal: None,
338 out_capable: true,
339 in_capable: true,
340 bitrate: self.bitrate,
341 airtime_profile: None,
342 announce_rate_target: None,
343 announce_rate_grace: 0,
344 announce_rate_penalty: 0.0,
345 announce_cap: constants::ANNOUNCE_CAP,
346 is_local_client: false,
347 wants_tunnel: false,
348 tunnel_id: None,
349 mtu: self.mtu,
350 ia_freq: 0.0,
351 ip_freq: 0.0,
352 op_freq: 0.0,
353 op_samples: 0,
354 started: 0.0,
355 ingress_control: self.ingress_control,
356 };
357
358 let registration = if let Some(template) = &self.dynamic_template {
361 Event::DynamicInterfaceUp {
362 id: client_id,
363 writer,
364 registration: Box::new(template.registration(info)),
365 }
366 } else {
367 Event::InterfaceUp(client_id, Some(writer), Some(Box::new(info)))
368 };
369 if self.tx.push_from_loop([registration]).is_err() {
370 return Admitted::DriverGone;
371 }
372
373 self.active_connections.fetch_add(1, Ordering::Relaxed);
374 Admitted::Client(
375 client_id,
376 Box::new(ClientSource {
377 stream,
378 id: client_id,
379 name: name.clone(),
380 tx: self.tx.clone(),
381 active_connections: Arc::clone(&self.active_connections),
382 decoder: hdlc::Decoder::reticulum(HW_MTU, self.ifac_size),
383 control: self.control.clone(),
384 }),
385 )
386 }
387}
388
389struct ClientSource {
391 stream: TcpStream,
392 id: InterfaceId,
393 name: String,
394 tx: EventSender,
395 active_connections: Arc<AtomicUsize>,
396 decoder: hdlc::Decoder,
397 control: ListenerControl,
398}
399
400impl LoopSource for ClientSource {
401 fn fd(&self) -> std::os::fd::RawFd {
402 self.stream.as_raw_fd()
403 }
404
405 fn stopped(&self) -> bool {
406 self.control.should_stop()
407 }
408
409 fn on_removed(&mut self) {
410 self.active_connections.fetch_sub(1, Ordering::Relaxed);
411 }
412
413 fn on_abandoned(&mut self) {
414 let _ = self.tx.push_from_loop([Event::InterfaceDown(self.id)]);
416 self.on_removed();
417 }
418
419 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
420 if self.control.should_stop() {
421 return SourceStatus::Remove;
422 }
423 match read_stream_frames(
424 self.stream.as_raw_fd(),
425 &mut self.decoder,
426 cx,
427 &self.tx,
428 self.id,
429 &self.name,
430 |_, _| {},
431 ) {
432 StreamRead::Rearm => SourceStatus::Rearm,
433 StreamRead::Suspend => SourceStatus::Suspend,
434 StreamRead::Closed => {
435 log::info!("[{}] client {} disconnected", self.name, self.id.0);
436 let _ = self.tx.push_from_loop([Event::InterfaceDown(self.id)]);
437 SourceStatus::Remove
438 }
439 StreamRead::Failed(e) => {
440 log::warn!("[{}] client {} read error: {}", self.name, self.id.0, e);
441 let _ = self.tx.push_from_loop([Event::InterfaceDown(self.id)]);
442 SourceStatus::Remove
443 }
444 StreamRead::DriverGone => SourceStatus::Remove,
445 }
446 }
447}
448
449use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
452use std::collections::HashMap;
453
454pub struct TcpServerFactory;
456
457impl InterfaceFactory for TcpServerFactory {
458 fn type_name(&self) -> &str {
459 "TCPServerInterface"
460 }
461
462 fn parse_config(
463 &self,
464 name: &str,
465 id: InterfaceId,
466 params: &HashMap<String, String>,
467 ) -> Result<Box<dyn InterfaceConfigData>, String> {
468 let listen_ip = params
469 .get("listen_ip")
470 .cloned()
471 .unwrap_or_else(|| "0.0.0.0".into());
472 let listen_port = params
473 .get("listen_port")
474 .and_then(|v| v.parse().ok())
475 .unwrap_or(4242);
476 let max_connections = params.get("max_connections").and_then(|v| v.parse().ok());
477 let (bitrate, fixed_mtu) = super::parse_tcp_rate_params(params)?;
478 let mut config = TcpServerConfig {
479 name: name.to_string(),
480 listen_ip,
481 listen_port,
482 interface_id: id,
483 max_connections,
484 ingress_control: IngressControlConfig::enabled(),
485 bitrate,
486 fixed_mtu,
487 runtime: Arc::new(Mutex::new(TcpServerRuntime {
488 max_connections: None,
489 })),
490 };
491 let startup = TcpServerRuntime::from_config(&config);
492 config.runtime = Arc::new(Mutex::new(startup));
493 Ok(Box::new(config))
494 }
495
496 fn start(
497 &self,
498 config: Box<dyn InterfaceConfigData>,
499 ctx: StartContext,
500 ) -> io::Result<StartResult> {
501 let mut cfg = *config
502 .into_any()
503 .downcast::<TcpServerConfig>()
504 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong config type"))?;
505 cfg.ingress_control = ctx.ingress_control;
506 let parent_id = cfg.interface_id;
507 let control = start_with_template(
508 cfg,
509 ctx.tx,
510 ctx.next_dynamic_id,
511 Some(super::DynamicInterfaceTemplate {
512 parent_id,
513 interface_type: "TCPServerClientInterface".into(),
514 ifac: ctx.ifac,
515 mode: ctx.mode,
516 gravity: ctx.gravity,
517 recursive_prs: ctx.recursive_prs,
518 announces_from_internal: ctx.announces_from_internal,
519 announces_to_internal: ctx.announces_to_internal,
520 }),
521 ctx.underlay_mark,
522 )?;
523 Ok(StartResult::Listener {
524 control: Some(control),
525 })
526 }
527}
528
529pub(crate) fn runtime_handle_from_config(config: &TcpServerConfig) -> TcpServerRuntimeConfigHandle {
530 TcpServerRuntimeConfigHandle {
531 interface_name: config.name.clone(),
532 runtime: Arc::clone(&config.runtime),
533 startup: TcpServerRuntime::from_config(config),
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540 use std::io::Read;
541 use std::net::TcpStream;
542 use std::sync::mpsc::RecvTimeoutError;
543 use std::thread;
544 use std::time::Duration;
545
546 fn find_free_port() -> u16 {
547 crate::test_support::port()
548 }
549
550 fn make_server_config(
551 port: u16,
552 interface_id: u64,
553 max_connections: Option<usize>,
554 ) -> TcpServerConfig {
555 let mut config = TcpServerConfig {
556 name: "test-server".into(),
557 listen_ip: "127.0.0.1".into(),
558 listen_port: port,
559 interface_id: InterfaceId(interface_id),
560 max_connections,
561 bitrate: None,
562 fixed_mtu: None,
563 ingress_control: IngressControlConfig::enabled(),
564 runtime: Arc::new(Mutex::new(TcpServerRuntime {
565 max_connections: None,
566 })),
567 };
568 let startup = TcpServerRuntime::from_config(&config);
569 config.runtime = Arc::new(Mutex::new(startup));
570 config
571 }
572
573 #[cfg(target_os = "linux")]
574 #[test]
575 fn stop_resume_preserves_partial_server_frame() {
576 use std::io::{BufRead, BufReader};
577 use std::process::{Child, Command, Stdio};
578 use std::sync::mpsc;
579
580 const CHILD_PORT: &str = "RNS_TEST_INTERRUPTED_TCP_SERVER_PORT";
581 const ROUNDS: usize = 3;
582 let payload = vec![0x7e; constants::HEADER_MINSIZE + 16];
583 if let Ok(port) = std::env::var(CHILD_PORT) {
584 let (tx, rx) = crate::event::channel();
585 let control = start(
586 make_server_config(port.parse().unwrap(), 1, None),
587 tx,
588 Arc::new(AtomicU64::new(1200)),
589 )
590 .unwrap();
591 println!("EINTR_READY");
592 let registration = rx.recv_timeout(Duration::from_secs(5)).unwrap();
593 assert!(matches!(
594 registration,
595 Event::InterfaceUp(InterfaceId(1200), _, _)
596 ));
597 println!("EINTR_CONNECTED");
598 for round in 0..ROUNDS {
599 let event = rx.recv_timeout(Duration::from_secs(5)).unwrap();
600 assert!(
601 matches!(event, Event::Frame { interface_id: InterfaceId(1200), data, .. } if data == payload),
602 "read interruption must preserve the connection and partial frame"
603 );
604 println!("EINTR_FRAME_{round}");
605 }
606 control.request_stop();
607 return;
608 }
609
610 struct ChildGuard(Child);
612 impl Drop for ChildGuard {
613 fn drop(&mut self) {
614 if matches!(self.0.try_wait(), Ok(None)) {
615 unsafe { libc::kill(self.0.id() as i32, libc::SIGCONT) };
616 let _ = self.0.kill();
617 let _ = self.0.wait();
618 }
619 }
620 }
621 let port = find_free_port();
622 let mut child = ChildGuard(
623 Command::new(std::env::current_exe().unwrap())
624 .args([
625 "interface::tcp_server::tests::stop_resume_preserves_partial_server_frame",
626 "--exact",
627 "--nocapture",
628 ])
629 .env(CHILD_PORT, port.to_string())
630 .stdout(Stdio::piped())
631 .spawn()
632 .unwrap(),
633 );
634 let stdout = child.0.stdout.take().unwrap();
635 let (lines_tx, lines_rx) = mpsc::channel();
636 let output = thread::spawn(move || {
637 for line in BufReader::new(stdout).lines() {
638 if lines_tx.send(line.unwrap()).is_err() {
639 break;
640 }
641 }
642 });
643 let wait_for = |expected: &str| {
644 let deadline = std::time::Instant::now() + Duration::from_secs(5);
645 loop {
646 let line = lines_rx
647 .recv_timeout(deadline.saturating_duration_since(std::time::Instant::now()))
648 .unwrap_or_else(|error| panic!("waiting for {expected}: {error}"));
649 if line == expected {
650 break;
651 }
652 }
653 };
654 wait_for("EINTR_READY");
655 let mut client = TcpStream::connect(("127.0.0.1", port)).unwrap();
656 wait_for("EINTR_CONNECTED");
657 let frame = hdlc::frame(&payload);
658 let split = frame.iter().position(|b| *b == 0x7d).unwrap() + 1;
660 for round in 0..ROUNDS {
661 client.write_all(&frame[..split]).unwrap();
662 thread::sleep(Duration::from_millis(30));
663 assert_eq!(unsafe { libc::kill(child.0.id() as i32, libc::SIGSTOP) }, 0);
664 thread::sleep(Duration::from_millis(200));
665 assert_eq!(unsafe { libc::kill(child.0.id() as i32, libc::SIGCONT) }, 0);
666 client.write_all(&frame[split..]).unwrap();
667 wait_for(&format!("EINTR_FRAME_{round}"));
668 }
669 assert!(child.0.wait().unwrap().success());
670 output.join().unwrap();
671 }
672
673 #[test]
674 fn accept_connection() {
675 let port = find_free_port();
676 let (tx, rx) = crate::event::channel();
677 let next_id = Arc::new(AtomicU64::new(1000));
678
679 let config = make_server_config(port, 1, None);
680
681 start(config, tx, next_id).unwrap();
682
683 thread::sleep(Duration::from_millis(50));
685
686 let _client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
688
689 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
691 match event {
692 Event::InterfaceUp(id, writer, info) => {
693 assert_eq!(id, InterfaceId(1000));
694 assert!(writer.is_some());
695 assert!(info.is_some());
696 }
697 other => panic!("expected InterfaceUp, got {:?}", other),
698 }
699 }
700
701 #[test]
702 fn spawned_client_drops_exact_header_minimum_and_recovers() {
703 let port = find_free_port();
704 let (tx, rx) = crate::event::channel();
705 let next_id = Arc::new(AtomicU64::new(1100));
706 start(make_server_config(port, 1, None), tx, next_id).unwrap();
707 thread::sleep(Duration::from_millis(50));
708
709 let mut client = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
710 assert!(matches!(
711 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
712 Event::InterfaceUp(InterfaceId(1100), _, _)
713 ));
714 client
715 .write_all(&hdlc::frame(&[0x11; rns_core::constants::HEADER_MINSIZE]))
716 .unwrap();
717 let valid = vec![0x22; rns_core::constants::HEADER_MINSIZE + 1];
718 client.write_all(&hdlc::frame(&valid)).unwrap();
719
720 assert!(matches!(
721 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
722 Event::Frame { interface_id: InterfaceId(1100), data, .. } if data == valid
723 ));
724 }
725
726 #[test]
727 fn typed_registration_carries_exact_type_mode_parent_and_ifac() {
728 let port = find_free_port();
729 let (tx, rx) = crate::event::channel();
730 let ifac = crate::ifac::derive_ifac(Some("network"), Some("key"), 8).unwrap();
731 let template = crate::interface::DynamicInterfaceTemplate {
732 parent_id: InterfaceId(77),
733 interface_type: "TCPServerClientInterface".into(),
734 ifac: Some(ifac),
735 mode: constants::MODE_GATEWAY,
736 gravity: -6,
737 recursive_prs: true,
738 announces_from_internal: false,
739 announces_to_internal: None,
740 };
741 start_with_template(
742 make_server_config(port, 77, None),
743 tx,
744 Arc::new(AtomicU64::new(1200)),
745 Some(template),
746 None,
747 )
748 .unwrap();
749 thread::sleep(Duration::from_millis(50));
750 let _client = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
751 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
752 Event::DynamicInterfaceUp { registration, .. } => {
753 assert_eq!(registration.parent_id, InterfaceId(77));
754 assert_eq!(registration.interface_type, "TCPServerClientInterface");
755 assert_eq!(registration.info.mode, constants::MODE_GATEWAY);
756 assert_eq!(registration.info.gravity, -6);
757 assert!(registration.info.recursive_prs);
758 assert!(!registration.info.announces_from_internal);
759 assert_eq!(registration.ifac.as_ref().map(|state| state.size), Some(8));
760 }
761 other => panic!("expected typed dynamic registration, got {other:?}"),
762 }
763 }
764
765 #[test]
766 fn spawned_client_inherits_ingress_control_config() {
767 let port = find_free_port();
768 let (tx, rx) = crate::event::channel();
769 let next_id = Arc::new(AtomicU64::new(1100));
770
771 let mut config = make_server_config(port, 11, None);
772 config.ingress_control = IngressControlConfig::disabled();
773 config.ingress_control.max_held_announces = 17;
774 config.ingress_control.burst_hold = 1.5;
775 config.ingress_control.burst_freq_new = 2.5;
776 config.ingress_control.burst_freq = 3.5;
777 config.ingress_control.new_time = 4.5;
778 config.ingress_control.burst_penalty = 5.5;
779 config.ingress_control.held_release_interval = 6.5;
780
781 start(config, tx, next_id).unwrap();
782 thread::sleep(Duration::from_millis(50));
783
784 let _client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
785
786 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
787 match event {
788 Event::InterfaceUp(_, _, Some(info)) => {
789 assert!(!info.ingress_control.enabled);
790 assert_eq!(info.ingress_control.max_held_announces, 17);
791 assert_eq!(info.ingress_control.burst_hold, 1.5);
792 assert_eq!(info.ingress_control.burst_freq_new, 2.5);
793 assert_eq!(info.ingress_control.burst_freq, 3.5);
794 assert_eq!(info.ingress_control.new_time, 4.5);
795 assert_eq!(info.ingress_control.burst_penalty, 5.5);
796 assert_eq!(info.ingress_control.held_release_interval, 6.5);
797 }
798 other => panic!("expected InterfaceUp with InterfaceInfo, got {:?}", other),
799 }
800 }
801
802 #[test]
803 fn listener_stop_prevents_new_accepts() {
804 let port = find_free_port();
805 let (tx, rx) = crate::event::channel();
806 let next_id = Arc::new(AtomicU64::new(1500));
807
808 let config = make_server_config(port, 15, None);
809 let control = start(config, tx, next_id).unwrap();
810
811 thread::sleep(Duration::from_millis(50));
812 control.request_stop();
813 thread::sleep(Duration::from_millis(120));
814
815 let connect_result = TcpStream::connect(format!("127.0.0.1:{}", port));
816 if let Ok(stream) = connect_result {
817 drop(stream);
818 }
819
820 match rx.recv_timeout(Duration::from_millis(200)) {
821 Err(RecvTimeoutError::Timeout) | Err(RecvTimeoutError::Disconnected) => {}
822 other => panic!(
823 "expected no InterfaceUp after listener stop, got {:?}",
824 other
825 ),
826 }
827 }
828
829 #[test]
830 fn receive_frame_from_client() {
831 let port = find_free_port();
832 let (tx, rx) = crate::event::channel();
833 let next_id = Arc::new(AtomicU64::new(2000));
834
835 let config = make_server_config(port, 2, None);
836
837 start(config, tx, next_id).unwrap();
838 thread::sleep(Duration::from_millis(50));
839
840 let mut client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
841
842 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
844
845 let payload: Vec<u8> = (0..32).collect();
847 let framed = hdlc::frame(&payload);
848 client.write_all(&framed).unwrap();
849
850 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
852 match event {
853 Event::Frame {
854 interface_id,
855 data,
856 rssi: _,
857 snr: _,
858 } => {
859 assert_eq!(interface_id, InterfaceId(2000));
860 assert_eq!(data, payload);
861 }
862 other => panic!("expected Frame, got {:?}", other),
863 }
864 }
865
866 #[test]
867 fn a_flood_into_a_small_queue_suspends_reading_without_losing_frames() {
868 const FRAMES: u32 = 3000;
869 let port = find_free_port();
870 let (tx, rx) = crate::event::channel_with_capacity(4);
871 let snapshot_tx = tx.clone();
872 start(
873 make_server_config(port, 4, None),
874 tx,
875 Arc::new(AtomicU64::new(4000)),
876 )
877 .unwrap();
878 let mut client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
879 assert!(matches!(
880 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
881 Event::InterfaceUp(InterfaceId(4000), _, _)
882 ));
883
884 let writer = thread::spawn(move || {
885 let mut wire = Vec::new();
886 for seq in 0..FRAMES {
887 let mut payload = vec![0u8; 32];
888 payload[2..18].copy_from_slice(&[0x5A; 16]);
889 payload[20..24].copy_from_slice(&seq.to_be_bytes());
890 wire.extend(hdlc::frame(&payload));
891 }
892 client.write_all(&wire).unwrap();
893 client
894 });
895
896 for expected in 0..FRAMES {
897 match rx.recv_timeout(Duration::from_secs(5)).unwrap() {
898 Event::Frame {
899 interface_id, data, ..
900 } => {
901 assert_eq!(interface_id, InterfaceId(4000));
902 let seq = u32::from_be_bytes(data[20..24].try_into().unwrap());
903 assert_eq!(seq, expected, "frames lost or reordered");
904 }
905 other => panic!("expected Frame, got {:?}", other),
906 }
907 }
908 let _client = writer.join().unwrap();
909 assert_eq!(snapshot_tx.inbound_queue_snapshot().dropped, [0, 0, 0, 0]);
910 }
911
912 #[test]
913 fn dropping_the_receiver_closes_the_listener() {
914 let port = find_free_port();
915 let (tx, rx) = crate::event::channel();
916 start(
917 make_server_config(port, 6, None),
918 tx,
919 Arc::new(AtomicU64::new(6000)),
920 )
921 .unwrap();
922 drop(rx);
923 let refused = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap_err();
924 assert_eq!(refused.kind(), io::ErrorKind::ConnectionRefused);
925 }
926
927 #[test]
928 fn a_stopped_listener_is_closed_at_the_next_sweep() {
929 let port = find_free_port();
930 let (tx, rx) = crate::event::channel();
931 let control = start(
932 make_server_config(port, 5, None),
933 tx,
934 Arc::new(AtomicU64::new(5000)),
935 )
936 .unwrap();
937 control.request_stop();
938 rx.sweep_stopped_sources();
939 let refused = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap_err();
940 assert_eq!(refused.kind(), io::ErrorKind::ConnectionRefused);
941 }
942
943 #[test]
944 fn accept_errors_map_to_drain_next_or_pause() {
945 use io::ErrorKind;
946 let step = |kind: ErrorKind| accept_error_step(&io::Error::from(kind));
947 assert_eq!(step(ErrorKind::WouldBlock), AcceptStep::Drained);
948 for kind in [
949 ErrorKind::Interrupted,
950 ErrorKind::ConnectionAborted,
951 ErrorKind::ConnectionReset,
952 ErrorKind::NetworkDown,
953 ErrorKind::NetworkUnreachable,
954 ErrorKind::HostUnreachable,
955 ] {
956 assert_eq!(step(kind), AcceptStep::Next, "{kind:?}");
957 }
958 assert_eq!(
959 accept_error_step(&io::Error::from_raw_os_error(libc::ECONNABORTED)),
960 AcceptStep::Next
961 );
962 assert_eq!(
963 accept_error_step(&io::Error::from_raw_os_error(libc::EPROTO)),
964 AcceptStep::Next
965 );
966 for errno in [libc::EMFILE, libc::ENFILE, libc::ENOBUFS, libc::ENOMEM] {
967 assert_eq!(
968 accept_error_step(&io::Error::from_raw_os_error(errno)),
969 AcceptStep::Pause,
970 "errno {errno}"
971 );
972 }
973 }
974
975 struct PauseOnce {
978 inner: ListenerSource,
979 paused: bool,
980 }
981
982 impl LoopSource for PauseOnce {
983 fn fd(&self) -> std::os::fd::RawFd {
984 self.inner.fd()
985 }
986 fn on_ready(&mut self, cx: &mut LoopContext<'_>) -> SourceStatus {
987 if !std::mem::replace(&mut self.paused, true) {
988 return SourceStatus::Pause;
989 }
990 self.inner.on_ready(cx)
991 }
992 }
993
994 fn serve_pass(rx: &crate::event::EventReceiver) {
995 let mut ready = polling::Events::new();
996 rx.wait(&mut ready, Some(Duration::from_millis(50)))
997 .unwrap();
998 rx.serve_sources(&ready);
999 }
1000
1001 #[test]
1002 fn connections_made_while_the_listener_is_paused_are_accepted_after_the_resume() {
1003 let (tx, rx) = crate::event::channel();
1004 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
1005 listener.set_nonblocking(true).unwrap();
1006 let addr = listener.local_addr().unwrap();
1007 let config = make_server_config(addr.port(), 8, None);
1008 let inner = ListenerSource {
1009 listener,
1010 name: config.name.clone(),
1011 tx: tx.clone(),
1012 next_id: Arc::new(AtomicU64::new(8000)),
1013 runtime: Arc::clone(&config.runtime),
1014 ingress_control: config.ingress_control,
1015 bitrate: None,
1016 mtu: crate::interface::TCP_DEFAULT_MTU,
1017 active_connections: Arc::new(AtomicUsize::new(0)),
1018 control: ListenerControl::new(),
1019 dynamic_template: None,
1020 ifac_size: 0,
1021 underlay_mark: None,
1022 };
1023 tx.register_source(
1024 crate::interface::loop_writer::read_key(config.interface_id),
1025 Box::new(PauseOnce {
1026 inner,
1027 paused: false,
1028 }),
1029 )
1030 .unwrap();
1031
1032 let _first = TcpStream::connect(addr).unwrap();
1033 serve_pass(&rx);
1034 let _second = TcpStream::connect(addr).unwrap();
1035 for _ in 0..3 {
1036 serve_pass(&rx);
1037 }
1038 assert!(
1039 rx.try_recv().is_err(),
1040 "a paused listener accepted a connection"
1041 );
1042
1043 rx.resume_paused_sources();
1044 let mut up = Vec::new();
1045 for _ in 0..20 {
1046 serve_pass(&rx);
1047 while let Ok(event) = rx.try_recv() {
1048 match event {
1049 Event::InterfaceUp(id, _, _) => up.push(id),
1050 other => panic!("expected InterfaceUp, got {:?}", other),
1051 }
1052 }
1053 if up.len() == 2 {
1054 break;
1055 }
1056 }
1057 assert_eq!(up, [InterfaceId(8000), InterfaceId(8001)]);
1058 }
1059
1060 fn sockopt_int(fd: std::os::fd::RawFd, level: libc::c_int, name: libc::c_int) -> libc::c_int {
1061 let mut value: libc::c_int = 0;
1062 let mut len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
1063 let rc = unsafe {
1065 libc::getsockopt(
1066 fd,
1067 level,
1068 name,
1069 &mut value as *mut _ as *mut libc::c_void,
1070 &mut len,
1071 )
1072 };
1073 assert_eq!(rc, 0, "getsockopt: {}", io::Error::last_os_error());
1074 value
1075 }
1076
1077 #[test]
1078 fn accepted_sockets_get_keepalive_and_timeout_options() {
1079 let port = find_free_port();
1080 let (tx, rx) = crate::event::channel();
1081 start(
1082 make_server_config(port, 9, None),
1083 tx,
1084 Arc::new(AtomicU64::new(9000)),
1085 )
1086 .unwrap();
1087 let _client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1088 let writer = match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
1089 Event::InterfaceUp(_, Some(writer), _) => writer,
1090 other => panic!("expected InterfaceUp with writer, got {:?}", other),
1091 };
1092 let fd = writer
1093 .loop_socket()
1094 .expect("server writer exposes its socket");
1095 assert_eq!(sockopt_int(fd, libc::SOL_SOCKET, libc::SO_KEEPALIVE), 1);
1096 assert_eq!(sockopt_int(fd, libc::IPPROTO_TCP, libc::TCP_NODELAY), 1);
1097 #[cfg(target_os = "linux")]
1098 assert_eq!(
1099 sockopt_int(fd, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT),
1100 24_000
1101 );
1102 }
1103
1104 #[test]
1105 fn send_frame_to_client() {
1106 let port = find_free_port();
1107 let (tx, rx) = crate::event::channel();
1108 let next_id = Arc::new(AtomicU64::new(3000));
1109
1110 let config = make_server_config(port, 3, None);
1111
1112 start(config, tx, next_id).unwrap();
1113 thread::sleep(Duration::from_millis(50));
1114
1115 let mut client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1116 client
1117 .set_read_timeout(Some(Duration::from_secs(2)))
1118 .unwrap();
1119
1120 let event = rx.recv_timeout(Duration::from_secs(1)).unwrap();
1122 let mut writer = match event {
1123 Event::InterfaceUp(_, Some(w), _) => w,
1124 other => panic!("expected InterfaceUp with writer, got {:?}", other),
1125 };
1126
1127 let payload: Vec<u8> = (0..24).collect();
1129 writer.send_frame(&payload).unwrap();
1130
1131 let mut buf = [0u8; 256];
1133 let n = client.read(&mut buf).unwrap();
1134 let expected = hdlc::frame(&payload);
1135 assert_eq!(&buf[..n], &expected[..]);
1136 }
1137
1138 #[test]
1139 fn multiple_clients() {
1140 let port = find_free_port();
1141 let (tx, rx) = crate::event::channel();
1142 let next_id = Arc::new(AtomicU64::new(4000));
1143
1144 let config = make_server_config(port, 4, None);
1145
1146 start(config, tx, next_id).unwrap();
1147 thread::sleep(Duration::from_millis(50));
1148
1149 let _client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1151 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1152
1153 let mut ids = Vec::new();
1155 for _ in 0..2 {
1156 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1157 match event {
1158 Event::InterfaceUp(id, _, _) => ids.push(id),
1159 other => panic!("expected InterfaceUp, got {:?}", other),
1160 }
1161 }
1162
1163 assert_eq!(ids.len(), 2);
1165 assert_ne!(ids[0], ids[1]);
1166 }
1167
1168 #[test]
1169 fn client_disconnect() {
1170 let port = find_free_port();
1171 let (tx, rx) = crate::event::channel();
1172 let next_id = Arc::new(AtomicU64::new(5000));
1173
1174 let config = make_server_config(port, 5, None);
1175
1176 start(config, tx, next_id).unwrap();
1177 thread::sleep(Duration::from_millis(50));
1178
1179 let client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1180
1181 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
1183
1184 drop(client);
1186
1187 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1189 assert!(
1190 matches!(event, Event::InterfaceDown(InterfaceId(5000))),
1191 "expected InterfaceDown(5000), got {:?}",
1192 event
1193 );
1194 }
1195
1196 #[test]
1197 fn server_bind_port() {
1198 let port = find_free_port();
1199 let (tx, _rx) = crate::event::channel();
1200 let next_id = Arc::new(AtomicU64::new(6000));
1201
1202 let config = make_server_config(port, 6, None);
1203
1204 start(config, tx, next_id).unwrap();
1206 }
1207
1208 #[test]
1209 fn max_connections_rejects_excess() {
1210 let port = find_free_port();
1211 let (tx, rx) = crate::event::channel();
1212 let next_id = Arc::new(AtomicU64::new(7000));
1213
1214 let config = make_server_config(port, 7, Some(2));
1215
1216 start(config, tx, next_id).unwrap();
1217 thread::sleep(Duration::from_millis(50));
1218
1219 let _client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1221 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1222
1223 for _ in 0..2 {
1225 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1226 assert!(matches!(event, Event::InterfaceUp(_, _, _)));
1227 }
1228
1229 let client3 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1231 client3
1232 .set_read_timeout(Some(Duration::from_millis(500)))
1233 .unwrap();
1234
1235 thread::sleep(Duration::from_millis(100));
1237
1238 let result = rx.recv_timeout(Duration::from_millis(500));
1240 assert!(
1241 result.is_err(),
1242 "expected no InterfaceUp for rejected connection, got {:?}",
1243 result
1244 );
1245 }
1246
1247 #[test]
1248 fn max_connections_allows_after_disconnect() {
1249 let port = find_free_port();
1250 let (tx, rx) = crate::event::channel();
1251 let next_id = Arc::new(AtomicU64::new(7100));
1252
1253 let config = make_server_config(port, 71, Some(1));
1254
1255 start(config, tx, next_id).unwrap();
1256 thread::sleep(Duration::from_millis(50));
1257
1258 let client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1260 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1261 assert!(matches!(event, Event::InterfaceUp(_, _, _)));
1262
1263 drop(client1);
1265
1266 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1268 assert!(matches!(event, Event::InterfaceDown(_)));
1269
1270 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1272 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1273 assert!(
1274 matches!(event, Event::InterfaceUp(_, _, _)),
1275 "expected InterfaceUp after slot freed, got {:?}",
1276 event
1277 );
1278 }
1279
1280 #[test]
1281 fn runtime_max_connections_updates_live() {
1282 let port = find_free_port();
1283 let (tx, rx) = crate::event::channel();
1284 let next_id = Arc::new(AtomicU64::new(7200));
1285
1286 let config = make_server_config(port, 72, None);
1287 let runtime = Arc::clone(&config.runtime);
1288
1289 start(config, tx, next_id).unwrap();
1290 thread::sleep(Duration::from_millis(50));
1291
1292 let _client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1293 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
1294 assert!(matches!(event, Event::InterfaceUp(_, _, _)));
1295
1296 {
1297 let mut runtime = runtime.lock().unwrap();
1298 runtime.max_connections = Some(1);
1299 }
1300
1301 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
1302 let result = rx.recv_timeout(Duration::from_millis(400));
1303 assert!(
1304 result.is_err(),
1305 "expected no InterfaceUp after lowering max_connections, got {:?}",
1306 result
1307 );
1308 }
1309}