1use std::net::Ipv4Addr;
57use crate::error::VCLError;
58use etherparse::{Ipv4HeaderSlice, Ipv6HeaderSlice};
59use tracing::{debug, info, warn};
60
61#[derive(Debug, Clone)]
63pub struct TunConfig {
64 pub name: String,
66 pub address: Ipv4Addr,
68 pub destination: Ipv4Addr,
70 pub netmask: Ipv4Addr,
72 pub mtu: u16,
74}
75
76impl Default for TunConfig {
77 fn default() -> Self {
78 TunConfig {
79 name: "vcl0".to_string(),
80 address: "10.0.0.1".parse().unwrap(),
81 destination: "10.0.0.2".parse().unwrap(),
82 netmask: "255.255.255.0".parse().unwrap(),
83 mtu: 1420,
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq)]
90pub enum IpVersion {
91 V4,
92 V6,
93 Unknown(u8),
94}
95
96#[derive(Debug, Clone)]
98pub struct IpPacket {
99 pub raw: Vec<u8>,
101 pub version: IpVersion,
103 pub src: String,
105 pub dst: String,
107 pub protocol: u8,
109 pub len: usize,
111}
112
113pub struct VCLTun {
117 #[cfg(target_os = "linux")]
118 dev: tun::AsyncDevice,
119 config: TunConfig,
120}
121
122impl VCLTun {
123 #[cfg(target_os = "linux")]
131 pub fn create(config: TunConfig) -> Result<Self, VCLError> {
132 let mut tun_config = tun::Configuration::default();
133 tun_config
134 .name(&config.name)
135 .address(config.address)
136 .destination(config.destination)
137 .netmask(config.netmask)
138 .mtu(config.mtu as i32)
139 .up();
140
141 let dev = tun::create_as_async(&tun_config)
142 .map_err(|e| VCLError::IoError(format!("Failed to create TUN device: {}", e)))?;
143
144 info!(
145 name = %config.name,
146 address = %config.address,
147 destination = %config.destination,
148 mtu = config.mtu,
149 "TUN interface created"
150 );
151
152 Ok(VCLTun { dev, config })
153 }
154
155 #[cfg(not(target_os = "linux"))]
157 pub fn create(_config: TunConfig) -> Result<Self, VCLError> {
158 Err(VCLError::IoError(
159 "TUN interface is only supported on Linux".to_string(),
160 ))
161 }
162
163 #[cfg(target_os = "linux")]
171 pub async fn read_packet(&mut self) -> Result<Vec<u8>, VCLError> {
172 use tokio::io::AsyncReadExt;
173 let mut buf = vec![0u8; self.config.mtu as usize + 4];
174 let n = self.dev.read(&mut buf).await
175 .map_err(|e| VCLError::IoError(format!("TUN read failed: {}", e)))?;
176 buf.truncate(n);
177 debug!(size = n, "TUN packet read");
178 Ok(buf)
179 }
180
181 #[cfg(not(target_os = "linux"))]
182 pub async fn read_packet(&mut self) -> Result<Vec<u8>, VCLError> {
183 Err(VCLError::IoError("TUN not supported on this platform".to_string()))
184 }
185
186 #[cfg(target_os = "linux")]
194 pub async fn write_packet(&mut self, packet: &[u8]) -> Result<(), VCLError> {
195 use tokio::io::AsyncWriteExt;
196 self.dev.write_all(packet).await
197 .map_err(|e| VCLError::IoError(format!("TUN write failed: {}", e)))?;
198 debug!(size = packet.len(), "TUN packet injected");
199 Ok(())
200 }
201
202 #[cfg(not(target_os = "linux"))]
203 pub async fn write_packet(&mut self, _packet: &[u8]) -> Result<(), VCLError> {
204 Err(VCLError::IoError("TUN not supported on this platform".to_string()))
205 }
206
207 pub fn name(&self) -> &str {
209 &self.config.name
210 }
211
212 pub fn mtu(&self) -> u16 {
214 self.config.mtu
215 }
216
217 pub fn address(&self) -> Ipv4Addr {
219 self.config.address
220 }
221
222 pub fn destination(&self) -> Ipv4Addr {
224 self.config.destination
225 }
226
227 pub fn config(&self) -> &TunConfig {
229 &self.config
230 }
231}
232
233pub fn parse_ip_packet(raw: Vec<u8>) -> Result<IpPacket, VCLError> {
243 if raw.is_empty() {
244 return Err(VCLError::InvalidPacket("Empty IP packet".to_string()));
245 }
246
247 let version_byte = raw[0] >> 4;
248 let len = raw.len();
249
250 match version_byte {
251 4 => parse_ipv4(raw, len),
252 6 => parse_ipv6(raw, len),
253 v => {
254 warn!(version = v, "Unknown IP version in TUN packet");
255 Ok(IpPacket {
256 raw,
257 version: IpVersion::Unknown(v),
258 src: String::new(),
259 dst: String::new(),
260 protocol: 0,
261 len,
262 })
263 }
264 }
265}
266
267fn parse_ipv4(raw: Vec<u8>, len: usize) -> Result<IpPacket, VCLError> {
268 let header = Ipv4HeaderSlice::from_slice(&raw)
269 .map_err(|e| VCLError::InvalidPacket(format!("IPv4 parse error: {}", e)))?;
270
271 let src = format!(
272 "{}.{}.{}.{}",
273 header.source()[0], header.source()[1],
274 header.source()[2], header.source()[3]
275 );
276 let dst = format!(
277 "{}.{}.{}.{}",
278 header.destination()[0], header.destination()[1],
279 header.destination()[2], header.destination()[3]
280 );
281 let protocol = header.protocol().0;
282
283 debug!(src = %src, dst = %dst, protocol, size = len, "IPv4 packet parsed");
284
285 Ok(IpPacket {
286 raw,
287 version: IpVersion::V4,
288 src,
289 dst,
290 protocol,
291 len,
292 })
293}
294
295fn parse_ipv6(raw: Vec<u8>, len: usize) -> Result<IpPacket, VCLError> {
296 let header = Ipv6HeaderSlice::from_slice(&raw)
297 .map_err(|e| VCLError::InvalidPacket(format!("IPv6 parse error: {}", e)))?;
298
299 let src = format!("{:?}", header.source_addr());
300 let dst = format!("{:?}", header.destination_addr());
301 let protocol = header.next_header().0;
302
303 debug!(src = %src, dst = %dst, protocol, size = len, "IPv6 packet parsed");
304
305 Ok(IpPacket {
306 raw,
307 version: IpVersion::V6,
308 src,
309 dst,
310 protocol,
311 len,
312 })
313}
314
315pub fn is_ipv4(raw: &[u8]) -> bool {
317 raw.first().map(|b| b >> 4 == 4).unwrap_or(false)
318}
319
320pub fn is_ipv6(raw: &[u8]) -> bool {
322 raw.first().map(|b| b >> 4 == 6).unwrap_or(false)
323}
324
325pub fn ip_version(raw: &[u8]) -> Option<IpVersion> {
327 raw.first().map(|b| match b >> 4 {
328 4 => IpVersion::V4,
329 6 => IpVersion::V6,
330 v => IpVersion::Unknown(v),
331 })
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 fn make_ipv4_packet() -> Vec<u8> {
339 vec![
341 0x45, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 192, 168, 1, 1, 10, 0, 0, 1, 0x00, 0x00, 0x00, 0x00, ]
353 }
354
355 fn make_ipv6_packet() -> Vec<u8> {
356 let mut pkt = vec![
358 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x11, 0x40, ];
363 pkt.extend_from_slice(&[0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1]);
365 pkt.extend_from_slice(&[0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2]);
367 pkt.extend_from_slice(&[0u8; 8]);
369 pkt
370 }
371
372 #[test]
373 fn test_tun_config_default() {
374 let c = TunConfig::default();
375 assert_eq!(c.name, "vcl0");
376 assert_eq!(c.mtu, 1420);
377 assert_eq!(c.address, "10.0.0.1".parse::<Ipv4Addr>().unwrap());
378 }
379
380 #[test]
381 fn test_parse_ipv4_packet() {
382 let raw = make_ipv4_packet();
383 let pkt = parse_ip_packet(raw).unwrap();
384 assert_eq!(pkt.version, IpVersion::V4);
385 assert_eq!(pkt.src, "192.168.1.1");
386 assert_eq!(pkt.dst, "10.0.0.1");
387 assert_eq!(pkt.protocol, 6); }
389
390 #[test]
391 fn test_parse_ipv6_packet() {
392 let raw = make_ipv6_packet();
393 let pkt = parse_ip_packet(raw).unwrap();
394 assert_eq!(pkt.version, IpVersion::V6);
395 assert_eq!(pkt.protocol, 17); }
397
398 #[test]
399 fn test_parse_empty_packet() {
400 let result = parse_ip_packet(vec![]);
401 assert!(result.is_err());
402 }
403
404 #[test]
405 fn test_parse_unknown_version() {
406 let raw = vec![0x30, 0x00, 0x00, 0x00]; let pkt = parse_ip_packet(raw).unwrap();
408 assert_eq!(pkt.version, IpVersion::Unknown(3));
409 }
410
411 #[test]
412 fn test_is_ipv4() {
413 assert!(is_ipv4(&make_ipv4_packet()));
414 assert!(!is_ipv4(&make_ipv6_packet()));
415 assert!(!is_ipv4(&[]));
416 }
417
418 #[test]
419 fn test_is_ipv6() {
420 assert!(is_ipv6(&make_ipv6_packet()));
421 assert!(!is_ipv6(&make_ipv4_packet()));
422 assert!(!is_ipv6(&[]));
423 }
424
425 #[test]
426 fn test_ip_version() {
427 assert_eq!(ip_version(&make_ipv4_packet()), Some(IpVersion::V4));
428 assert_eq!(ip_version(&make_ipv6_packet()), Some(IpVersion::V6));
429 assert_eq!(ip_version(&[0x30]), Some(IpVersion::Unknown(3)));
430 assert_eq!(ip_version(&[]), None);
431 }
432
433 #[test]
434 fn test_tun_create_non_linux() {
435 #[cfg(not(target_os = "linux"))]
437 {
438 let result = VCLTun::create(TunConfig::default());
439 assert!(result.is_err());
440 }
441 #[cfg(target_os = "linux")]
443 {
444 let c = TunConfig::default();
446 assert_eq!(c.mtu, 1420);
447 }
448 }
449
450 #[test]
451 fn test_ip_packet_len() {
452 let raw = make_ipv4_packet();
453 let expected_len = raw.len();
454 let pkt = parse_ip_packet(raw).unwrap();
455 assert_eq!(pkt.len, expected_len);
456 }
457}