sans_io_runtime/
backend.rs1#[cfg(feature = "udp")]
2use std::net::SocketAddr;
3use std::{fmt::Debug, sync::Arc, time::Duration};
4
5#[cfg(feature = "poll-backend")]
6mod poll;
7
8#[cfg(feature = "polling-backend")]
9mod polling;
10
11#[cfg(feature = "poll-backend")]
12pub use poll::PollBackend;
13
14#[cfg(feature = "polling-backend")]
15pub use polling::PollingBackend;
16
17use crate::Buffer;
18
19#[cfg(feature = "tun-tap")]
20use self::tun::TunFd;
21
22#[derive(Debug)]
23pub enum BackendIncoming {
24 #[cfg(feature = "udp")]
25 UdpListenResult {
26 bind: SocketAddr,
27 result: Result<(SocketAddr, usize), std::io::Error>,
28 },
29 #[cfg(feature = "udp")]
30 UdpPacket {
31 slot: usize,
32 from: SocketAddr,
33 data: Buffer,
34 },
35 #[cfg(feature = "tun-tap")]
36 TunBindResult {
37 result: Result<usize, std::io::Error>,
38 },
39 #[cfg(feature = "tun-tap")]
40 TunPacket { slot: usize, data: Buffer },
41}
42
43#[derive(Debug)]
45pub enum BackendIncomingInternal<Owner> {
46 Event(Owner, BackendIncoming),
47 Awake,
48}
49
50#[derive(Debug, PartialEq, Eq)]
52pub enum BackendOutgoing {
53 #[cfg(feature = "udp")]
54 UdpListen { addr: SocketAddr, reuse: bool },
55 #[cfg(feature = "udp")]
56 UdpUnlisten { slot: usize },
57 #[cfg(feature = "udp")]
58 UdpPacket {
59 slot: usize,
60 to: SocketAddr,
61 data: Buffer,
62 },
63 #[cfg(feature = "udp")]
64 UdpPackets {
65 slot: usize,
66 to: Vec<SocketAddr>,
67 data: Buffer,
68 },
69 #[cfg(feature = "udp")]
70 UdpPackets2 {
71 to: Vec<(usize, SocketAddr)>,
72 data: Buffer,
73 },
74 #[cfg(feature = "tun-tap")]
75 TunBind { fd: TunFd },
76 #[cfg(feature = "tun-tap")]
77 TunUnbind { slot: usize },
78 #[cfg(feature = "tun-tap")]
79 TunPacket { slot: usize, data: Buffer },
80}
81
82pub trait Awaker: Send + Sync {
83 fn awake(&self);
84}
85
86pub trait Backend<Owner>: Default + BackendOwner<Owner> {
87 fn create_awaker(&self) -> Arc<dyn Awaker>;
88 fn poll_incoming(&mut self, timeout: Duration);
89 fn pop_incoming(&mut self) -> Option<BackendIncomingInternal<Owner>>;
90 fn finish_outgoing_cycle(&mut self);
91 fn finish_incoming_cycle(&mut self);
92}
93
94pub trait BackendOwner<Owner> {
95 fn on_action(&mut self, owner: Owner, action: BackendOutgoing);
96}
97
98#[cfg(feature = "tun-tap")]
99pub mod tun {
100 use std::net::Ipv4Addr;
101
102 use tun::{
103 platform::{posix::Fd, Device},
104 IntoAddress,
105 };
106
107 pub struct TunFd {
108 pub fd: Fd,
109 pub read: bool,
110 }
111
112 impl Eq for TunFd {}
113
114 impl PartialEq for TunFd {
115 fn eq(&self, other: &Self) -> bool {
116 self.fd.0 == other.fd.0
117 }
118 }
119
120 impl std::fmt::Debug for TunFd {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 write!(f, "TunFd({})", self.fd.0)
123 }
124 }
125
126 pub struct TunDevice {
127 device: Device,
128 }
129
130 impl TunDevice {
131 pub fn get_queue_fd(&mut self, index: usize) -> Option<TunFd> {
132 use std::os::fd::AsRawFd;
133 use tun::Device;
134
135 #[cfg(target_os = "macos")]
136 let queue = self.device.queue(0)?;
137 #[cfg(target_os = "linux")]
138 let queue = self.device.queue(index)?;
139 queue.set_nonblock().expect("Should set tun nonblock");
140 let raw_fd = queue.as_raw_fd();
141 Some(TunFd {
142 fd: Fd(raw_fd),
143 #[cfg(target_os = "macos")]
144 read: index == 0,
145 #[cfg(target_os = "linux")]
146 read: true,
147 })
148 }
149 }
150
151 impl Drop for TunDevice {
152 fn drop(&mut self) {
153 log::info!("Dropping tun device");
154 }
155 }
156
157 pub fn create_tun<A: IntoAddress>(
158 name: &str,
159 ip: A,
160 netmask: A,
161 mtu: u16,
162 queues: usize,
163 ) -> TunDevice {
164 let mut config = tun::Configuration::default();
165 let ip: Ipv4Addr = ip.into_address().expect("Should convert to ip-v4");
166 let netmask: Ipv4Addr = netmask.into_address().expect("Should convert to ip-v4");
167 log::info!(
168 "Creating tun device with ip: {} and netmask: {}",
169 ip,
170 netmask
171 );
172 config
173 .name(name)
174 .address(ip)
175 .destination(ip)
176 .netmask(netmask)
177 .mtu(mtu as i32)
178 .up();
179
180 #[cfg(target_os = "linux")]
181 config.queues(queues);
182 #[cfg(not(target_os = "linux"))]
183 log::info!("Ignoring queues on non-linux platform, setting as {queues} but override to 1");
184
185 let device = tun::create(&config).expect("Should create tun device");
186 device
187 .set_nonblock()
188 .expect("Should set vpn tun device nonblock");
189
190 #[cfg(any(target_os = "macos", target_os = "ios"))]
191 {
192 use std::process::Command;
193 use tun::Device as _;
194
195 let ip_addr = device.address().expect("Should have address");
196 let output = Command::new("route")
200 .args([
201 "-n",
202 "add",
203 "-net",
204 "10.33.33.0/24",
205 &format!("{}", ip_addr),
206 ])
207 .output();
208 match output {
209 Ok(output) => {
210 if !output.status.success() {
211 log::error!(
212 "Add route to tun device error {}",
213 String::from_utf8_lossy(&output.stderr)
214 );
215 } else {
216 log::info!("Add route to tun device success");
217 }
218 }
219 Err(e) => {
220 log::error!("Add route to tune device error {}", e);
221 }
222 }
223 }
224
225 TunDevice { device }
226 }
227}