sans_io_runtime/
backend.rs

1#[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/// Represents an incoming network event.
44#[derive(Debug)]
45pub enum BackendIncomingInternal<Owner> {
46    Event(Owner, BackendIncoming),
47    Awake,
48}
49
50/// Represents an outgoing network control.
51#[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 netmask = device.netmask().expect("Should have netmask");
197
198            //TODO avoid using fixed value
199            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}