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