use crate::dissect::{Dissect, DissectResult};
use crate::format::pcap::mask::IPV4_FRAG_OFFSET;
use crate::Error;
#[derive(Debug, Clone)]
pub struct Ipv4<'a> {
data: &'a [u8],
}
impl<'a> Ipv4<'a> {
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
if data.len() < 20 {
return Err(Error::truncated(20, data.len()));
}
let version = data[0] >> 4;
if version != 4 {
return Err(Error::parse(
0,
format!("Invalid IPv4 version: {}", version),
));
}
Ok(Self { data })
}
pub fn version(&self) -> u8 {
self.data[0] >> 4
}
pub fn ihl(&self) -> usize {
((self.data[0] & 0x0f) * 4) as usize
}
pub fn tos(&self) -> u8 {
self.data[1]
}
pub fn total_len(&self) -> u16 {
u16::from_be_bytes([self.data[2], self.data[3]])
}
pub fn identification(&self) -> u16 {
u16::from_be_bytes([self.data[4], self.data[5]])
}
pub fn df(&self) -> bool {
(self.data[6] & 0x40) != 0
}
pub fn mf(&self) -> bool {
(self.data[6] & 0x20) != 0
}
pub fn fragment_offset(&self) -> u16 {
let flags = u16::from_be_bytes([self.data[6], self.data[7]]);
flags & IPV4_FRAG_OFFSET
}
pub fn ttl(&self) -> u8 {
self.data[8]
}
pub fn protocol(&self) -> u8 {
self.data[9]
}
pub fn checksum(&self) -> u16 {
u16::from_be_bytes([self.data[10], self.data[11]])
}
pub fn src(&self) -> [u8; 4] {
let mut ip = [0u8; 4];
ip.copy_from_slice(&self.data[12..16]);
ip
}
pub fn dst(&self) -> [u8; 4] {
let mut ip = [0u8; 4];
ip.copy_from_slice(&self.data[16..20]);
ip
}
pub fn src_str(&self) -> String {
let ip = self.src();
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
}
pub fn dst_str(&self) -> String {
let ip = self.dst();
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
}
pub fn payload(&self) -> &'a [u8] {
&self.data[self.ihl()..]
}
}
impl<'a> Dissect<'a> for Ipv4<'a> {
type Output = &'a [u8];
fn dissect(&self) -> DissectResult<Self::Output> {
Ok(self.payload())
}
fn data(&self) -> &'a [u8] {
self.data
}
fn header_len(&self) -> usize {
self.ihl()
}
}
pub mod protocol {
pub const ICMP: u8 = 1;
pub const TCP: u8 = 6;
pub const UDP: u8 = 17;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ipv4_new_valid() {
let data = vec![
0x45, 0x00, 0x00, 0x28, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02, ];
let ip = Ipv4::new(&data).unwrap();
assert_eq!(ip.version(), 4);
assert_eq!(ip.ihl(), 20);
assert_eq!(ip.protocol(), 6);
}
#[test]
fn test_ipv4_new_truncated() {
let data = vec![0u8; 10];
let result = Ipv4::new(&data);
assert!(result.is_err());
}
#[test]
fn test_ipv4_new_invalid_version() {
let mut data = vec![0u8; 20];
data[0] = 0x60; let result = Ipv4::new(&data);
assert!(result.is_err());
}
#[test]
fn test_ipv4_addresses() {
let data = vec![
0x45, 0x00, 0x00, 0x28, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x00, 0x00, 0xc0, 0xa8,
0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02, ];
let ip = Ipv4::new(&data).unwrap();
assert_eq!(ip.src_str(), "192.168.1.1");
assert_eq!(ip.dst_str(), "192.168.1.2");
}
#[test]
fn test_ipv4_payload() {
let mut data = vec![
0x45, 0x00, 0x00, 0x28, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x00, 0x00, 0xc0, 0xa8,
0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02,
];
data.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]); let ip = Ipv4::new(&data).unwrap();
assert_eq!(ip.payload(), &[0xde, 0xad, 0xbe, 0xef]);
}
}