ios_core/tunnel/
manager.rs1use std::collections::HashMap;
2use std::sync::Arc;
3
4use tokio::sync::{watch, RwLock};
5
6#[cfg(feature = "tunnel-userspace")]
7use crate::tunnel::tun::userspace::UserspaceTunDevice;
8use crate::tunnel::TunnelInfo;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum TunMode {
13 Kernel,
15 #[default]
17 Userspace,
18}
19
20pub struct TunnelHandle {
22 pub udid: String,
23 pub info: TunnelInfo,
24 pub userspace_port: Option<u16>,
25 _runtime: TunnelRuntime,
26}
27
28enum TunnelRuntime {
29 Kernel {
30 _cancel_tx: watch::Sender<()>,
32 },
33 #[cfg(feature = "tunnel-userspace")]
34 Userspace { _runtime: UserspaceTunDevice },
35}
36
37impl TunnelHandle {
38 pub fn new(
39 udid: String,
40 info: TunnelInfo,
41 userspace_port: Option<u16>,
42 ) -> (Self, watch::Receiver<()>) {
43 let (tx, rx) = watch::channel(());
44 (
45 Self {
46 udid,
47 info,
48 userspace_port,
49 _runtime: TunnelRuntime::Kernel { _cancel_tx: tx },
50 },
51 rx,
52 )
53 }
54
55 #[cfg(feature = "tunnel-userspace")]
56 pub fn new_userspace(udid: String, info: TunnelInfo, runtime: UserspaceTunDevice) -> Self {
57 Self {
58 udid,
59 info,
60 userspace_port: Some(runtime.local_port),
61 _runtime: TunnelRuntime::Userspace { _runtime: runtime },
62 }
63 }
64
65 pub fn is_alive(&self) -> bool {
66 match &self._runtime {
67 TunnelRuntime::Kernel { _cancel_tx } => _cancel_tx.receiver_count() > 0,
68 #[cfg(feature = "tunnel-userspace")]
69 TunnelRuntime::Userspace { _runtime } => _runtime.is_alive(),
70 }
71 }
72}
73
74#[derive(Clone)]
76pub struct TunnelManager {
77 tunnels: Arc<RwLock<HashMap<String, Arc<TunnelHandle>>>>,
78 pub mode: TunMode,
79}
80
81impl TunnelManager {
82 pub fn new(mode: TunMode) -> Self {
83 Self {
84 tunnels: Arc::new(RwLock::new(HashMap::new())),
85 mode,
86 }
87 }
88
89 pub async fn register(&self, handle: Arc<TunnelHandle>) {
90 self.tunnels
91 .write()
92 .await
93 .insert(handle.udid.clone(), handle);
94 }
95
96 pub async fn list(&self) -> Vec<Arc<TunnelHandle>> {
97 self.tunnels.read().await.values().cloned().collect()
98 }
99
100 pub async fn find(&self, udid: &str) -> Option<Arc<TunnelHandle>> {
101 self.tunnels.read().await.get(udid).cloned()
102 }
103
104 pub async fn stop(&self, udid: &str) -> bool {
106 self.tunnels.write().await.remove(udid).is_some()
107 }
108}
109
110impl Default for TunnelManager {
111 fn default() -> Self {
112 Self::new(TunMode::Userspace)
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use std::sync::Arc;
119
120 use super::*;
121
122 fn tunnel_info() -> TunnelInfo {
123 TunnelInfo {
124 server_address: "fd59:2381:6956::1".into(),
125 server_rsd_port: 58783,
126 client_address: "fd59:2381:6956::2".into(),
127 client_mtu: 1280,
128 }
129 }
130
131 #[test]
132 fn tunnel_handle_kernel_liveness_tracks_cancel_receiver() {
133 let (handle, cancel_rx) = TunnelHandle::new("test-udid".into(), tunnel_info(), None);
134
135 assert!(handle.is_alive());
136
137 drop(cancel_rx);
138 assert!(!handle.is_alive());
139 }
140
141 #[tokio::test]
142 async fn tunnel_manager_register_find_list_and_stop() {
143 let manager = TunnelManager::new(TunMode::Kernel);
144 let (handle, cancel_rx) = TunnelHandle::new("test-udid".into(), tunnel_info(), None);
145 let handle = Arc::new(handle);
146
147 manager.register(handle.clone()).await;
148
149 assert!(Arc::ptr_eq(
150 &manager.find("test-udid").await.unwrap(),
151 &handle
152 ));
153 assert!(manager.find("missing").await.is_none());
154 assert_eq!(manager.list().await.len(), 1);
155 assert_eq!(manager.list().await[0].udid, "test-udid");
156
157 assert!(manager.stop("test-udid").await);
158 assert!(!manager.stop("test-udid").await);
159 assert!(manager.find("test-udid").await.is_none());
160 assert!(manager.list().await.is_empty());
161
162 drop(cancel_rx);
163 }
164
165 #[test]
166 fn tunnel_manager_default_uses_userspace_mode() {
167 let manager = TunnelManager::default();
168 assert_eq!(manager.mode, TunMode::Userspace);
169 }
170}