1use std::net::{Ipv4Addr, Ipv6Addr};
7use std::sync::atomic::{AtomicU32, Ordering};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
11#[repr(u8)]
12pub enum Protocol {
13 #[default]
15 Unknown = 0,
16 Icmp = 1,
18 Tcp = 6,
20 Udp = 17,
22 Icmpv6 = 58,
24}
25
26impl From<u8> for Protocol {
27 fn from(value: u8) -> Self {
28 match value {
29 1 => Self::Icmp,
30 6 => Self::Tcp,
31 17 => Self::Udp,
32 58 => Self::Icmpv6,
33 _ => Self::Unknown,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
40#[repr(u8)]
41pub enum IpVersion {
42 #[default]
44 Unknown = 0,
45 V4 = 4,
47 V6 = 6,
49}
50
51#[repr(C)]
56#[derive(Debug, Clone, Copy, Default)]
57pub struct PacketMetadata {
58 pub l2_offset: u16,
60 pub l3_offset: u16,
62 pub l4_offset: u16,
64 pub protocol: Protocol,
66 pub ip_version: IpVersion,
68 pub flow_hash: u64,
70 pub src_port: u16,
72 pub dst_port: u16,
74 pub flags: u8,
76 _padding: [u8; 3],
78}
79
80impl PacketMetadata {
81 #[inline]
83 #[must_use]
84 pub const fn new() -> Self {
85 Self {
86 l2_offset: 0,
87 l3_offset: 0,
88 l4_offset: 0,
89 protocol: Protocol::Unknown,
90 ip_version: IpVersion::Unknown,
91 flow_hash: 0,
92 src_port: 0,
93 dst_port: 0,
94 flags: 0,
95 _padding: [0; 3],
96 }
97 }
98
99 #[inline]
101 #[must_use]
102 pub const fn is_tcp(&self) -> bool {
103 matches!(self.protocol, Protocol::Tcp)
104 }
105
106 #[inline]
108 #[must_use]
109 pub const fn is_udp(&self) -> bool {
110 matches!(self.protocol, Protocol::Udp)
111 }
112
113 #[inline]
115 #[must_use]
116 pub const fn is_icmp(&self) -> bool {
117 matches!(self.protocol, Protocol::Icmp | Protocol::Icmpv6)
118 }
119}
120
121#[repr(C, align(64))]
138pub struct ZeroCopyPacket {
139 data: *const u8,
141 len: u32,
143 metadata: PacketMetadata,
145 refcount: AtomicU32,
147 desc_idx: u16,
149 flags: u16,
151 timestamp: u64,
153}
154
155unsafe impl Send for ZeroCopyPacket {}
157unsafe impl Sync for ZeroCopyPacket {}
158
159impl Default for ZeroCopyPacket {
160 fn default() -> Self {
161 Self::empty()
162 }
163}
164
165impl ZeroCopyPacket {
166 pub const FLAG_NEEDS_CSUM: u16 = 1 << 0;
168 pub const FLAG_GSO: u16 = 1 << 1;
170 pub const FLAG_FROM_GUEST: u16 = 1 << 2;
172 pub const FLAG_TO_GUEST: u16 = 1 << 3;
174
175 #[inline]
177 #[must_use]
178 pub const fn empty() -> Self {
179 Self {
180 data: std::ptr::null(),
181 len: 0,
182 metadata: PacketMetadata::new(),
183 refcount: AtomicU32::new(0),
184 desc_idx: 0,
185 flags: 0,
186 timestamp: 0,
187 }
188 }
189
190 #[inline]
199 #[must_use]
200 pub const unsafe fn from_raw_parts(data: *const u8, len: u32, desc_idx: u16) -> Self {
201 Self {
202 data,
203 len,
204 metadata: PacketMetadata::new(),
205 refcount: AtomicU32::new(1),
206 desc_idx,
207 flags: 0,
208 timestamp: 0,
209 }
210 }
211
212 #[inline]
218 #[must_use]
219 pub const unsafe fn from_slice(data: &[u8], desc_idx: u16) -> Self {
220 Self {
221 data: data.as_ptr(),
222 len: data.len() as u32,
223 metadata: PacketMetadata::new(),
224 refcount: AtomicU32::new(1),
225 desc_idx,
226 flags: 0,
227 timestamp: 0,
228 }
229 }
230
231 #[inline]
233 #[must_use]
234 pub const fn is_empty(&self) -> bool {
235 self.len == 0 || self.data.is_null()
236 }
237
238 #[inline]
240 #[must_use]
241 pub const fn len(&self) -> usize {
242 self.len as usize
243 }
244
245 #[inline]
251 #[must_use]
252 pub unsafe fn as_slice(&self) -> &[u8] {
253 if self.data.is_null() {
254 &[]
255 } else {
256 unsafe { std::slice::from_raw_parts(self.data, self.len as usize) }
258 }
259 }
260
261 #[inline]
263 #[must_use]
264 pub const fn data_ptr(&self) -> *const u8 {
265 self.data
266 }
267
268 #[inline]
270 #[must_use]
271 pub const fn desc_idx(&self) -> u16 {
272 self.desc_idx
273 }
274
275 #[inline]
277 #[must_use]
278 pub const fn metadata(&self) -> &PacketMetadata {
279 &self.metadata
280 }
281
282 #[inline]
284 #[must_use]
285 pub fn metadata_mut(&mut self) -> &mut PacketMetadata {
286 &mut self.metadata
287 }
288
289 #[inline]
291 pub fn set_metadata(&mut self, metadata: PacketMetadata) {
292 self.metadata = metadata;
293 }
294
295 #[inline]
297 #[must_use]
298 pub const fn flags(&self) -> u16 {
299 self.flags
300 }
301
302 #[inline]
304 pub fn set_flags(&mut self, flags: u16) {
305 self.flags = flags;
306 }
307
308 #[inline]
310 pub fn add_flag(&mut self, flag: u16) {
311 self.flags |= flag;
312 }
313
314 #[inline]
316 #[must_use]
317 pub const fn has_flag(&self, flag: u16) -> bool {
318 self.flags & flag != 0
319 }
320
321 #[inline]
323 #[must_use]
324 pub const fn timestamp(&self) -> u64 {
325 self.timestamp
326 }
327
328 #[inline]
330 pub fn set_timestamp(&mut self, timestamp: u64) {
331 self.timestamp = timestamp;
332 }
333
334 #[inline]
336 pub fn add_ref(&self) {
337 self.refcount.fetch_add(1, Ordering::AcqRel);
338 }
339
340 #[inline]
342 pub fn release(&self) -> bool {
343 self.refcount.fetch_sub(1, Ordering::AcqRel) == 1
344 }
345
346 #[inline]
348 #[must_use]
349 pub fn refcount(&self) -> u32 {
350 self.refcount.load(Ordering::Acquire)
351 }
352
353 pub unsafe fn parse_headers(&mut self) {
359 if self.len < 14 {
360 return; }
362
363 let data = unsafe { std::slice::from_raw_parts(self.data, self.len as usize) };
366
367 self.metadata.l2_offset = 0;
369 self.metadata.l3_offset = 14;
370
371 let ethertype = u16::from_be_bytes([data[12], data[13]]);
373
374 match ethertype {
375 0x0800 => {
376 self.metadata.ip_version = IpVersion::V4;
378 self.parse_ipv4(data, 14);
379 }
380 0x86DD => {
381 self.metadata.ip_version = IpVersion::V6;
383 self.parse_ipv6(data, 14);
384 }
385 0x8100 => {
386 self.metadata.l3_offset = 18;
388 if self.len >= 18 {
389 let inner_ethertype = u16::from_be_bytes([data[16], data[17]]);
390 match inner_ethertype {
391 0x0800 => {
392 self.metadata.ip_version = IpVersion::V4;
393 self.parse_ipv4(data, 18);
394 }
395 0x86DD => {
396 self.metadata.ip_version = IpVersion::V6;
397 self.parse_ipv6(data, 18);
398 }
399 _ => {}
400 }
401 }
402 }
403 _ => {}
404 }
405
406 self.metadata.flow_hash = self.calculate_flow_hash();
408 }
409
410 fn parse_ipv4(&mut self, data: &[u8], offset: usize) {
412 if data.len() < offset + 20 {
413 return; }
415
416 let ihl = (data[offset] & 0x0F) as usize * 4;
417 self.metadata.l4_offset = (offset + ihl) as u16;
418 self.metadata.protocol = Protocol::from(data[offset + 9]);
419
420 let l4_offset = self.metadata.l4_offset as usize;
422 if data.len() >= l4_offset + 4 {
423 match self.metadata.protocol {
424 Protocol::Tcp | Protocol::Udp => {
425 self.metadata.src_port =
426 u16::from_be_bytes([data[l4_offset], data[l4_offset + 1]]);
427 self.metadata.dst_port =
428 u16::from_be_bytes([data[l4_offset + 2], data[l4_offset + 3]]);
429
430 if self.metadata.protocol == Protocol::Tcp && data.len() >= l4_offset + 14 {
432 self.metadata.flags = data[l4_offset + 13];
433 }
434 }
435 _ => {}
436 }
437 }
438 }
439
440 fn parse_ipv6(&mut self, data: &[u8], offset: usize) {
442 if data.len() < offset + 40 {
443 return; }
445
446 self.metadata.l4_offset = (offset + 40) as u16;
447 self.metadata.protocol = Protocol::from(data[offset + 6]); let l4_offset = self.metadata.l4_offset as usize;
451 if data.len() >= l4_offset + 4 {
452 match self.metadata.protocol {
453 Protocol::Tcp | Protocol::Udp => {
454 self.metadata.src_port =
455 u16::from_be_bytes([data[l4_offset], data[l4_offset + 1]]);
456 self.metadata.dst_port =
457 u16::from_be_bytes([data[l4_offset + 2], data[l4_offset + 3]]);
458
459 if self.metadata.protocol == Protocol::Tcp && data.len() >= l4_offset + 14 {
461 self.metadata.flags = data[l4_offset + 13];
462 }
463 }
464 _ => {}
465 }
466 }
467 }
468
469 fn calculate_flow_hash(&self) -> u64 {
471 let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
473
474 hash ^= self.metadata.protocol as u64;
475 hash = hash.wrapping_mul(0x0100_0000_01b3);
476
477 hash ^= self.metadata.src_port as u64;
478 hash = hash.wrapping_mul(0x0100_0000_01b3);
479
480 hash ^= self.metadata.dst_port as u64;
481 hash = hash.wrapping_mul(0x0100_0000_01b3);
482
483 unsafe {
485 let data = self.as_slice();
486 if self.metadata.ip_version == IpVersion::V4 {
487 let l3 = self.metadata.l3_offset as usize;
488 if data.len() >= l3 + 20 {
489 for i in 0..4 {
491 hash ^= data[l3 + 12 + i] as u64;
492 hash = hash.wrapping_mul(0x0100_0000_01b3);
493 }
494 for i in 0..4 {
496 hash ^= data[l3 + 16 + i] as u64;
497 hash = hash.wrapping_mul(0x0100_0000_01b3);
498 }
499 }
500 } else if self.metadata.ip_version == IpVersion::V6 {
501 let l3 = self.metadata.l3_offset as usize;
502 if data.len() >= l3 + 40 {
503 for i in 0..16 {
505 hash ^= data[l3 + 8 + i] as u64;
506 hash = hash.wrapping_mul(0x0100_0000_01b3);
507 }
508 for i in 0..16 {
510 hash ^= data[l3 + 24 + i] as u64;
511 hash = hash.wrapping_mul(0x0100_0000_01b3);
512 }
513 }
514 }
515 }
516
517 hash
518 }
519
520 #[must_use]
526 pub unsafe fn src_ipv4(&self) -> Option<Ipv4Addr> {
527 if self.metadata.ip_version != IpVersion::V4 {
528 return None;
529 }
530 let data = unsafe { self.as_slice() };
532 let l3 = self.metadata.l3_offset as usize;
533 if data.len() >= l3 + 20 {
534 Some(Ipv4Addr::new(
535 data[l3 + 12],
536 data[l3 + 13],
537 data[l3 + 14],
538 data[l3 + 15],
539 ))
540 } else {
541 None
542 }
543 }
544
545 #[must_use]
551 pub unsafe fn dst_ipv4(&self) -> Option<Ipv4Addr> {
552 if self.metadata.ip_version != IpVersion::V4 {
553 return None;
554 }
555 let data = unsafe { self.as_slice() };
557 let l3 = self.metadata.l3_offset as usize;
558 if data.len() >= l3 + 20 {
559 Some(Ipv4Addr::new(
560 data[l3 + 16],
561 data[l3 + 17],
562 data[l3 + 18],
563 data[l3 + 19],
564 ))
565 } else {
566 None
567 }
568 }
569
570 #[must_use]
576 pub unsafe fn src_ipv6(&self) -> Option<Ipv6Addr> {
577 if self.metadata.ip_version != IpVersion::V6 {
578 return None;
579 }
580 let data = unsafe { self.as_slice() };
582 let l3 = self.metadata.l3_offset as usize;
583 if data.len() >= l3 + 40 {
584 let mut octets = [0u8; 16];
585 octets.copy_from_slice(&data[l3 + 8..l3 + 24]);
586 Some(Ipv6Addr::from(octets))
587 } else {
588 None
589 }
590 }
591
592 #[must_use]
598 pub unsafe fn dst_ipv6(&self) -> Option<Ipv6Addr> {
599 if self.metadata.ip_version != IpVersion::V6 {
600 return None;
601 }
602 let data = unsafe { self.as_slice() };
604 let l3 = self.metadata.l3_offset as usize;
605 if data.len() >= l3 + 40 {
606 let mut octets = [0u8; 16];
607 octets.copy_from_slice(&data[l3 + 24..l3 + 40]);
608 Some(Ipv6Addr::from(octets))
609 } else {
610 None
611 }
612 }
613}
614
615impl std::fmt::Debug for ZeroCopyPacket {
616 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617 f.debug_struct("ZeroCopyPacket")
618 .field("data", &self.data)
619 .field("len", &self.len)
620 .field("metadata", &self.metadata)
621 .field("refcount", &self.refcount.load(Ordering::Relaxed))
622 .field("desc_idx", &self.desc_idx)
623 .field("flags", &self.flags)
624 .field("timestamp", &self.timestamp)
625 .finish()
626 }
627}
628
629#[cfg(test)]
630mod tests {
631 use super::*;
632
633 #[test]
634 fn test_packet_size() {
635 assert!(std::mem::size_of::<ZeroCopyPacket>() <= 128);
637 assert_eq!(std::mem::align_of::<ZeroCopyPacket>(), 64);
639 }
640
641 #[test]
642 fn test_empty_packet() {
643 let pkt = ZeroCopyPacket::empty();
644 assert!(pkt.is_empty());
645 assert_eq!(pkt.len(), 0);
646 assert_eq!(pkt.refcount(), 0);
647 }
648
649 #[test]
650 fn test_protocol_from() {
651 assert_eq!(Protocol::from(1), Protocol::Icmp);
652 assert_eq!(Protocol::from(6), Protocol::Tcp);
653 assert_eq!(Protocol::from(17), Protocol::Udp);
654 assert_eq!(Protocol::from(58), Protocol::Icmpv6);
655 assert_eq!(Protocol::from(255), Protocol::Unknown);
656 }
657
658 #[test]
659 fn test_metadata() {
660 let mut meta = PacketMetadata::new();
661 meta.protocol = Protocol::Tcp;
662 meta.src_port = 12345;
663 meta.dst_port = 80;
664
665 assert!(meta.is_tcp());
666 assert!(!meta.is_udp());
667 assert!(!meta.is_icmp());
668 }
669
670 #[test]
671 fn test_packet_from_slice() {
672 let data = [0u8; 64];
673 let pkt = unsafe { ZeroCopyPacket::from_slice(&data, 42) };
674
675 assert!(!pkt.is_empty());
676 assert_eq!(pkt.len(), 64);
677 assert_eq!(pkt.desc_idx(), 42);
678 assert_eq!(pkt.refcount(), 1);
679 }
680
681 #[test]
682 fn test_refcount() {
683 let data = [0u8; 64];
684 let pkt = unsafe { ZeroCopyPacket::from_slice(&data, 0) };
685
686 assert_eq!(pkt.refcount(), 1);
687
688 pkt.add_ref();
689 assert_eq!(pkt.refcount(), 2);
690
691 assert!(!pkt.release());
692 assert_eq!(pkt.refcount(), 1);
693
694 assert!(pkt.release());
695 assert_eq!(pkt.refcount(), 0);
696 }
697
698 #[test]
699 fn test_flags() {
700 let mut pkt = ZeroCopyPacket::empty();
701
702 assert_eq!(pkt.flags(), 0);
703 assert!(!pkt.has_flag(ZeroCopyPacket::FLAG_NEEDS_CSUM));
704
705 pkt.add_flag(ZeroCopyPacket::FLAG_NEEDS_CSUM);
706 assert!(pkt.has_flag(ZeroCopyPacket::FLAG_NEEDS_CSUM));
707 assert!(!pkt.has_flag(ZeroCopyPacket::FLAG_GSO));
708
709 pkt.add_flag(ZeroCopyPacket::FLAG_GSO);
710 assert!(pkt.has_flag(ZeroCopyPacket::FLAG_NEEDS_CSUM));
711 assert!(pkt.has_flag(ZeroCopyPacket::FLAG_GSO));
712 }
713}