Skip to main content

fips_core/upper/
tun_write.rs

1use std::collections::VecDeque;
2use std::sync::{Arc, Condvar, Mutex, MutexGuard, mpsc};
3
4use crate::transport::PacketBuffer;
5
6#[derive(Debug)]
7struct TunWriteQueue {
8    state: Mutex<TunWriteState>,
9    ready: Condvar,
10}
11
12#[derive(Debug)]
13struct TunWriteState {
14    priority: VecDeque<TunWritePacket>,
15    senders: usize,
16    receiver_alive: bool,
17}
18
19#[derive(Debug)]
20#[cfg_attr(
21    not(any(test, target_os = "linux", target_os = "macos", windows)),
22    allow(dead_code)
23)]
24pub(crate) enum TunWritePacket {
25    Vec(Vec<u8>),
26    Pooled(PacketBuffer),
27}
28
29impl TunWritePacket {
30    #[cfg_attr(
31        not(any(test, target_os = "linux", target_os = "macos", windows)),
32        allow(dead_code)
33    )]
34    pub(crate) fn as_slice(&self) -> &[u8] {
35        match self {
36            Self::Vec(packet) => packet,
37            Self::Pooled(packet) => packet.as_slice(),
38        }
39    }
40
41    #[cfg(any(test, target_os = "linux", target_os = "macos", windows))]
42    pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] {
43        match self {
44            Self::Vec(packet) => packet,
45            Self::Pooled(packet) => packet.as_mut_slice(),
46        }
47    }
48
49    #[cfg(any(test, target_os = "linux", target_os = "macos", windows))]
50    pub(crate) fn len(&self) -> usize {
51        self.as_slice().len()
52    }
53}
54
55impl From<Vec<u8>> for TunWritePacket {
56    fn from(packet: Vec<u8>) -> Self {
57        Self::Vec(packet)
58    }
59}
60
61impl From<PacketBuffer> for TunWritePacket {
62    fn from(packet: PacketBuffer) -> Self {
63        Self::Pooled(packet)
64    }
65}
66
67/// Channel sender for packets to be written to TUN.
68#[derive(Debug)]
69pub struct TunTx {
70    queue: Arc<TunWriteQueue>,
71}
72
73/// Channel receiver consumed by the blocking TUN writer.
74#[derive(Debug)]
75#[cfg(any(test, target_os = "linux", target_os = "macos", windows))]
76pub(crate) struct TunRx {
77    queue: Arc<TunWriteQueue>,
78}
79
80impl Clone for TunTx {
81    fn clone(&self) -> Self {
82        {
83            let mut state = self.queue.lock();
84            state.senders = state.senders.saturating_add(1);
85        }
86        Self {
87            queue: Arc::clone(&self.queue),
88        }
89    }
90}
91
92impl Drop for TunTx {
93    fn drop(&mut self) {
94        let should_notify = {
95            let mut state = self.queue.lock();
96            state.senders = state.senders.saturating_sub(1);
97            state.senders == 0
98        };
99        if should_notify {
100            self.queue.ready.notify_all();
101        }
102    }
103}
104
105impl TunTx {
106    /// Queue a priority packet for TUN delivery.
107    pub fn send(&self, packet: Vec<u8>) -> Result<(), mpsc::SendError<Vec<u8>>> {
108        let mut state = self.queue.lock();
109        if !state.receiver_alive {
110            return Err(mpsc::SendError(packet));
111        }
112
113        state.priority.push_back(TunWritePacket::Vec(packet));
114        drop(state);
115        self.queue.ready.notify_one();
116        Ok(())
117    }
118
119    /// Queue a batch of packets for TUN delivery. Returns the number of
120    /// packets dropped because the receiver is closed.
121    pub(crate) fn send_batch<I, P>(&self, packets: I) -> usize
122    where
123        I: IntoIterator<Item = P>,
124        P: Into<TunWritePacket>,
125    {
126        let packets = packets.into_iter();
127        let mut state = self.queue.lock();
128        if !state.receiver_alive {
129            return packets.count();
130        }
131
132        let mut sent = 0;
133        for packet in packets {
134            state.priority.push_back(packet.into());
135            sent += 1;
136        }
137        drop(state);
138        if sent > 0 {
139            self.queue.ready.notify_one();
140        }
141        0
142    }
143}
144
145#[cfg(any(test, target_os = "linux", target_os = "macos", windows))]
146impl TunRx {
147    pub(crate) fn recv(&self) -> Option<TunWritePacket> {
148        let mut state = self.queue.lock();
149        loop {
150            if let Some(packet) = state.priority.pop_front() {
151                return Some(packet);
152            }
153            if state.senders == 0 {
154                state.receiver_alive = false;
155                return None;
156            }
157            state = self
158                .queue
159                .ready
160                .wait(state)
161                .unwrap_or_else(|poisoned| poisoned.into_inner());
162        }
163    }
164
165    #[cfg(any(test, target_os = "linux"))]
166    pub(crate) fn try_recv_packet(&self) -> Result<TunWritePacket, mpsc::TryRecvError> {
167        let mut state = self.queue.lock();
168        if let Some(packet) = state.priority.pop_front() {
169            return Ok(packet);
170        }
171        if state.senders == 0 {
172            state.receiver_alive = false;
173            Err(mpsc::TryRecvError::Disconnected)
174        } else {
175            Err(mpsc::TryRecvError::Empty)
176        }
177    }
178}
179
180#[cfg(any(test, target_os = "linux", target_os = "macos", windows))]
181impl Drop for TunRx {
182    fn drop(&mut self) {
183        let mut state = self.queue.lock();
184        state.receiver_alive = false;
185        state.priority.clear();
186        drop(state);
187        self.queue.ready.notify_all();
188    }
189}
190
191impl TunWriteQueue {
192    fn lock(&self) -> MutexGuard<'_, TunWriteState> {
193        self.state
194            .lock()
195            .unwrap_or_else(|poisoned| poisoned.into_inner())
196    }
197}
198
199#[cfg(any(test, target_os = "linux", target_os = "macos", windows))]
200pub(crate) fn write_channel() -> (TunTx, TunRx) {
201    let queue = Arc::new(TunWriteQueue {
202        state: Mutex::new(TunWriteState {
203            priority: VecDeque::new(),
204            senders: 1,
205            receiver_alive: true,
206        }),
207        ready: Condvar::new(),
208    });
209    (
210        TunTx {
211            queue: Arc::clone(&queue),
212        },
213        TunRx { queue },
214    )
215}