about summary refs log tree commit diff
path: root/src/ippacket.rs
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2025-05-17 09:53:23 -0500
committergennyble <gen@nyble.dev>2025-05-17 09:53:23 -0500
commit2bd66b9a04be8a63d7ee0be2d633ead183fa7e76 (patch)
tree8962e5bc5b0e8ac1af7f01f0db875bc60d8d44c3 /src/ippacket.rs
downloadskim-2bd66b9a04be8a63d7ee0be2d633ead183fa7e76.tar.gz
skim-2bd66b9a04be8a63d7ee0be2d633ead183fa7e76.zip
initial commit; meow
Diffstat (limited to 'src/ippacket.rs')
-rw-r--r--src/ippacket.rs99
1 files changed, 99 insertions, 0 deletions
diff --git a/src/ippacket.rs b/src/ippacket.rs
new file mode 100644
index 0000000..60858d2
--- /dev/null
+++ b/src/ippacket.rs
@@ -0,0 +1,99 @@
+use std::net::Ipv4Addr;
+
+pub struct Ipv4Packet<'p> {
+	/// The computed size of the IP packet
+	header_length: usize,
+	/// The total length of the IP packet when all fragments are combined
+	total_length: u16,
+	more_fragments: bool,
+	fragment_offset: usize,
+	next_header: IpNextHeader,
+	pub src: Ipv4Addr,
+	pub dst: Ipv4Addr,
+	payload: &'p [u8],
+}
+
+impl<'p> Ipv4Packet<'p> {
+	pub fn new(buffer: &'p [u8]) -> Self {
+		let ihl = buffer[0].to_be() & 0b0000_1111;
+		let header_length = ihl as usize * 4;
+
+		let total_length = u16::from_be_bytes([buffer[2], buffer[3]]);
+
+		// Fragmentation
+		let more_fragments = (buffer[6] & 0b0010_0000) > 0;
+		let fragment_offset = u16::from_be_bytes([buffer[6] & 0b0001_1111, buffer[7]]);
+		// Fragments are in units of 8 bytes
+		let true_frag_offset = fragment_offset as usize * 8;
+
+		let next_header = IpNextHeader::new(buffer[9]);
+
+		let src = Ipv4Addr::new(buffer[12], buffer[13], buffer[14], buffer[15]);
+		let dst = Ipv4Addr::new(buffer[16], buffer[17], buffer[18], buffer[19]);
+
+		Self {
+			header_length,
+			total_length,
+			more_fragments,
+			fragment_offset: true_frag_offset,
+			next_header,
+			src,
+			dst,
+			payload: &buffer[header_length..],
+		}
+	}
+
+	pub fn get_source(&self) -> Ipv4Addr {
+		self.src
+	}
+
+	pub fn get_destination(&self) -> Ipv4Addr {
+		self.dst
+	}
+
+	pub fn has_more_fragments(&self) -> bool {
+		self.more_fragments
+	}
+
+	pub fn get_fragment_offset(&self) -> usize {
+		self.fragment_offset
+	}
+
+	pub fn get_next_header(&self) -> IpNextHeader {
+		self.next_header
+	}
+
+	pub fn get_payload(&self) -> &[u8] {
+		&self.payload
+	}
+
+	pub fn get_packet_len(&self) -> usize {
+		self.total_length as usize
+	}
+
+	pub fn get_payload_len(&self) -> usize {
+		// An IPv4 header is always 20 bytes long
+		self.total_length as usize - 20
+	}
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum IpNextHeader {
+	Icmp,
+	Igmp,
+	Tcp,
+	Udp,
+	Unknown(u8),
+}
+
+impl IpNextHeader {
+	pub fn new(n: u8) -> Self {
+		match n {
+			1 => Self::Icmp,
+			2 => Self::Igmp,
+			6 => Self::Tcp,
+			17 => Self::Udp,
+			n => Self::Unknown(n),
+		}
+	}
+}