pub mod opt;
use std::fmt::{self, Formatter};
use std::net::Ipv4Addr;
use std::ops::Deref;
use zerocopy::byteorder::{BigEndian, U16, U32};
use zerocopy::{FromBytes, IntoBytes, Unaligned};
use crate::packet::ipv4::opt::Ipv4OptionsIter;
use crate::packet::protocol::IpProto;
use crate::packet::{HeaderParser, PacketHeader};
#[repr(C, packed)]
#[derive(
FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
)]
pub struct Ipv4Header {
ver_ihl: u8,
dscp_ecn: u8,
total_length: U16<BigEndian>,
identification: U16<BigEndian>,
flags_frag_offset: U16<BigEndian>,
ttl: u8,
protocol: IpProto,
checksum: U16<BigEndian>,
src_ip: U32<BigEndian>,
dst_ip: U32<BigEndian>,
}
impl Ipv4Header {
#[inline]
pub fn dscp(&self) -> u8 {
self.dscp_ecn >> 2
}
#[inline]
pub fn ecn(&self) -> u8 {
self.dscp_ecn & 0x03
}
#[inline]
pub fn version(&self) -> u8 {
self.ver_ihl >> 4
}
#[inline]
pub fn ihl(&self) -> u8 {
self.ver_ihl & 0x0F
}
const OFFSET_MASK: u16 = 0x1FFF;
const MF_FLAG_MASK: u16 = 0x2000;
const DF_FLAG_MASK: u16 = 0x4000;
const RS_FLAG_MASK: u16 = 0x8000;
#[inline]
pub fn flags(&self) -> u8 {
(self.flags_frag_offset.get() >> 13) as u8
}
#[inline]
pub fn fragment_offset(&self) -> u16 {
self.flags_frag_offset.get() & Self::OFFSET_MASK
}
#[inline]
pub fn has_dont_fragment(&self) -> bool {
(self.flags_frag_offset.get() & Self::DF_FLAG_MASK) != 0
}
#[inline]
pub fn has_more_fragment(&self) -> bool {
(self.flags_frag_offset.get() & Self::MF_FLAG_MASK) != 0
}
#[inline]
pub fn has_reserved_flag(&self) -> bool {
(self.flags_frag_offset.get() & Self::RS_FLAG_MASK) != 0
}
#[inline]
pub fn is_fragmenting(&self) -> bool {
(self.flags_frag_offset.get() & (Self::MF_FLAG_MASK | Self::OFFSET_MASK)) != 0
}
#[inline]
pub fn is_first_fragment(&self) -> bool {
let raw = self.flags_frag_offset.get();
(raw & Self::MF_FLAG_MASK) != 0 && (raw & Self::OFFSET_MASK) == 0
}
#[inline]
pub fn is_last_fragment(&self) -> bool {
let raw = self.flags_frag_offset.get();
(raw & Self::MF_FLAG_MASK) == 0 && (raw & Self::OFFSET_MASK) != 0
}
#[inline]
pub fn total_length(&self) -> usize {
self.total_length.get() as usize
}
#[inline]
pub fn ttl(&self) -> u8 {
self.ttl
}
#[inline]
pub fn protocol(&self) -> IpProto {
self.protocol
}
#[inline]
pub fn src_ip(&self) -> Ipv4Addr {
Ipv4Addr::from(self.src_ip.get())
}
#[inline]
pub fn dst_ip(&self) -> Ipv4Addr {
Ipv4Addr::from(self.dst_ip.get())
}
#[inline]
pub fn src_ip_raw(&self) -> [u8; 4] {
self.src_ip.get().to_be_bytes()
}
#[inline]
pub fn dst_ip_raw(&self) -> [u8; 4] {
self.dst_ip.get().to_be_bytes()
}
#[inline]
pub fn has_options(&self) -> bool {
self.ihl() > 5
}
#[inline]
pub fn id(&self) -> u16 {
self.identification.get()
}
}
#[derive(Debug, Clone)]
pub struct Ipv4HeaderOpt<'a> {
pub header: &'a Ipv4Header,
pub raw_options: &'a [u8],
}
impl<'a> Ipv4HeaderOpt<'a> {
pub fn options(&'a self) -> Ipv4OptionsIter<'a> {
Ipv4OptionsIter::new(self.raw_options)
}
}
impl Deref for Ipv4HeaderOpt<'_> {
type Target = Ipv4Header;
#[inline]
fn deref(&self) -> &Self::Target {
self.header
}
}
impl PacketHeader for Ipv4Header {
const NAME: &'static str = "IPv4Header";
type InnerType = IpProto;
#[inline]
fn inner_type(&self) -> Self::InnerType {
self.protocol
}
#[inline]
fn total_len(&self, _buf: &[u8]) -> usize {
(self.ihl() as usize) * 4
}
#[inline]
fn is_valid(&self) -> bool {
self.version() == 4 && self.ihl() >= 5
}
}
impl HeaderParser for Ipv4Header {
type Output<'a> = Ipv4HeaderOpt<'a>;
#[inline]
fn into_view<'a>(header: &'a Self, raw_options: &'a [u8]) -> Self::Output<'a> {
Ipv4HeaderOpt {
header,
raw_options,
}
}
}
impl fmt::Display for Ipv4Header {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"IPv4 {} -> {} proto={} ttl={} len={}",
self.src_ip(),
self.dst_ip(),
self.protocol(),
self.ttl(),
self.total_length()
)?;
if self.is_fragmenting() {
write!(f, " frag offset={}", self.fragment_offset())?;
}
if self.has_options() {
write!(f, " +opts")?;
}
Ok(())
}
}
impl fmt::Display for Ipv4HeaderOpt<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.header)?;
if !self.raw_options.is_empty() {
write!(f, " opts=[")?;
let mut first = true;
for opt in self.options().flatten() {
if !first {
write!(f, ",")?;
}
first = false;
write!(f, "{}", opt)?;
}
write!(f, "]")?;
}
Ok(())
}
}
mod tests {
use super::*;
#[test]
fn test_ipv4_header_size() {
assert_eq!(std::mem::size_of::<Ipv4Header>(), 20);
assert_eq!(Ipv4Header::FIXED_LEN, 20);
}
#[test]
fn test_ipv4_version_and_ihl() {
let header = create_test_header();
assert_eq!(header.version(), 4);
assert_eq!(header.ihl(), 5); assert!(header.is_valid());
}
#[test]
fn test_ipv4_dscp_ecn() {
let mut header = create_test_header();
header.dscp_ecn = 0xB9; assert_eq!(header.dscp(), 46);
assert_eq!(header.ecn(), 1);
}
#[test]
fn test_ipv4_total_length() {
let mut header = create_test_header();
header.total_length = U16::new(1500);
assert_eq!(header.total_length(), 1500);
}
#[test]
fn test_ipv4_fragmentation() {
let mut header = create_test_header();
header.flags_frag_offset = U16::new(0x4000); assert_eq!(header.flags(), 0x02);
assert_eq!(header.fragment_offset(), 0);
assert!(!header.is_fragmenting());
header.flags_frag_offset = U16::new(0x20B9);
assert_eq!(header.flags(), 0x01); assert_eq!(header.fragment_offset(), 185);
assert!(header.is_fragmenting());
}
#[test]
fn test_ipv4_addresses() {
let header = create_test_header();
let expected_src = Ipv4Addr::new(192, 168, 1, 100);
assert_eq!(header.src_ip(), expected_src);
assert_eq!(header.src_ip_raw(), [192, 168, 1, 100]);
let expected_dst = Ipv4Addr::new(10, 0, 0, 1);
assert_eq!(header.dst_ip(), expected_dst);
assert_eq!(header.dst_ip_raw(), [10, 0, 0, 1]);
}
#[test]
fn test_ipv4_protocol_ttl() {
let header = create_test_header();
assert_eq!(header.protocol(), IpProto::TCP);
assert_eq!(header.ttl(), 64);
}
#[test]
fn test_ipv4_parsing_basic() {
let packet = create_test_packet();
let result = Ipv4Header::from_bytes(&packet);
assert!(result.is_ok());
let (header_ext, payload) = result.unwrap();
assert_eq!(header_ext.version(), 4);
assert_eq!(header_ext.ihl(), 5);
assert_eq!(header_ext.protocol(), IpProto::TCP);
assert!(!header_ext.has_options());
assert_eq!(header_ext.raw_options.len(), 0);
assert_eq!(payload.len(), 0); }
#[test]
fn test_ipv4_parsing_invalid_version() {
let mut packet = create_test_packet();
packet[0] = 0x60;
let result = Ipv4Header::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_ipv4_parsing_invalid_ihl() {
let mut packet = create_test_packet();
packet[0] = 0x44;
let result = Ipv4Header::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_ipv4_parsing_too_small() {
let packet = vec![0u8; 19];
let result = Ipv4Header::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_ipv4_total_len_no_options() {
let packet = create_test_packet();
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.total_len(&packet), 20);
assert_eq!(header_ext.raw_options.len(), 0);
}
#[test]
fn test_ipv4_with_options() {
let mut packet = create_test_packet();
packet[0] = 0x46;
packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.ihl(), 6);
assert_eq!(header_ext.total_len(&packet), 24); assert!(header_ext.is_valid());
assert!(header_ext.has_options());
assert_eq!(header_ext.raw_options.len(), 4);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 4); }
#[test]
fn test_ipv4_with_timestamp_option() {
let mut packet = create_test_packet();
packet[0] = 0x49;
packet.push(0x44); packet.push(0x10); packet.push(0x05); packet.push(0x00);
packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x02]); packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x03]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.ihl(), 9);
assert_eq!(header_ext.total_len(&packet), 36); assert!(header_ext.is_valid());
assert_eq!(header_ext.raw_options.len(), 16);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 1);
}
#[test]
fn test_ipv4_with_record_route_option() {
let mut packet = create_test_packet();
packet[0] = 0x4A;
packet.push(0x07); packet.push(0x13); packet.push(0x04);
packet.extend_from_slice(&[192, 168, 1, 1]);
packet.extend_from_slice(&[192, 168, 1, 2]);
packet.extend_from_slice(&[192, 168, 1, 3]);
packet.extend_from_slice(&[192, 168, 1, 4]);
packet.push(0x01);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.ihl(), 10);
assert_eq!(header_ext.total_len(&packet), 40); assert!(header_ext.is_valid());
assert_eq!(header_ext.raw_options.len(), 20);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 2); }
#[test]
fn test_ipv4_from_bytes_with_options_and_payload() {
let mut packet = create_test_packet();
packet[0] = 0x47;
packet.push(0x01); packet.push(0x82); packet.push(0x06); packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); packet.push(0x01);
let payload_data = b"Test payload after IP options";
packet.extend_from_slice(payload_data);
let total_len = 28 + payload_data.len();
packet[2] = ((total_len >> 8) & 0xFF) as u8;
packet[3] = (total_len & 0xFF) as u8;
let result = Ipv4Header::from_bytes(&packet);
assert!(result.is_ok());
let (header_ext, payload) = result.unwrap();
assert_eq!(payload.len(), payload_data.len());
assert_eq!(payload, payload_data);
assert_eq!(header_ext.ihl(), 7);
assert_eq!(header_ext.total_len(&packet), 28);
assert_eq!(header_ext.total_length(), total_len);
assert_eq!(header_ext.raw_options.len(), 8);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 3); }
#[test]
fn test_ipv4_total_len_includes_options() {
let mut packet = create_test_packet();
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.total_len(&packet), 20);
assert_eq!(header_ext.ihl(), 5);
assert_eq!(header_ext.raw_options.len(), 0);
packet = create_test_packet();
packet[0] = 0x46;
packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.total_len(&packet), 24); assert_eq!(header_ext.ihl(), 6);
assert_eq!(header_ext.raw_options.len(), 4);
packet = create_test_packet();
packet[0] = 0x47;
packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.total_len(&packet), 28); assert_eq!(header_ext.ihl(), 7);
assert_eq!(header_ext.raw_options.len(), 8);
packet = create_test_packet();
packet[0] = 0x4F; let options = vec![0x01u8; 40]; packet.extend_from_slice(&options);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.total_len(&packet), 60); assert_eq!(header_ext.ihl(), 15);
assert_eq!(header_ext.raw_options.len(), 40);
}
#[test]
fn test_ipv4_header_ext_with_options() {
let mut packet = create_test_packet();
packet[0] = 0x47;
packet.extend_from_slice(&[148, 0x04, 0x00, 0x00]);
packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_options.len(), 8);
assert!(header_ext.has_options());
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 5);
assert_eq!(header_ext.version(), 4);
assert_eq!(header_ext.ihl(), 7);
assert_eq!(header_ext.protocol(), IpProto::TCP);
}
#[test]
fn test_ipv4_header_ext_deref() {
let packet = create_test_packet();
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.version(), 4);
assert_eq!(header_ext.ihl(), 5);
assert_eq!(header_ext.dscp(), 0);
assert_eq!(header_ext.ecn(), 0);
assert_eq!(header_ext.ttl(), 64);
assert_eq!(header_ext.protocol(), IpProto::TCP);
assert_eq!(
header_ext.src_ip(),
"192.168.1.100".parse::<Ipv4Addr>().unwrap()
);
assert_eq!(header_ext.dst_ip(), "10.0.0.1".parse::<Ipv4Addr>().unwrap());
}
#[test]
fn test_ipv4_options_integration_record_route() {
let mut packet = create_test_packet();
packet[0] = 0x48;
packet.push(0x07); packet.push(0x0B); packet.push(0x04); packet.extend_from_slice(&[192, 168, 1, 1]); packet.extend_from_slice(&[192, 168, 1, 2]);
packet.push(0x01);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_options.len(), 12);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 2);
use crate::packet::ipv4::opt::Ipv4OptionElement;
match &opts[0] {
Ipv4OptionElement::RecordRoute {
pointer,
route_data,
} => {
assert_eq!(*pointer, 4);
assert_eq!(route_data.len(), 8);
assert!(!opts[0].is_copied());
}
_ => panic!("Expected RecordRoute option"),
}
}
#[test]
fn test_ipv4_options_integration_timestamp() {
let mut packet = create_test_packet();
packet[0] = 0x47;
packet.push(0x44); packet.push(0x08); packet.push(0x05); packet.push(0x00); packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_options.len(), 8);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 1);
use crate::packet::ipv4::opt::{Ipv4OptionElement, TimestampFlag};
match &opts[0] {
Ipv4OptionElement::Timestamp {
pointer,
overflow,
flags,
data,
} => {
assert_eq!(*pointer, 5);
assert_eq!(*overflow, 0);
assert_eq!(*flags, TimestampFlag::TimestampsOnly);
assert_eq!(data.len(), 4);
assert_eq!(opts[0].option_class(), 2);
}
_ => panic!("Expected Timestamp option"),
}
}
#[test]
fn test_ipv4_options_integration_security() {
let mut packet = create_test_packet();
packet[0] = 0x47;
packet.push(0x82); packet.push(0x06); packet.extend_from_slice(&[0x00, 0xAB, 0x00, 0xCD]);
packet.extend_from_slice(&[0x01, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_options.len(), 8);
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 3);
use crate::packet::ipv4::opt::Ipv4OptionElement;
match &opts[0] {
Ipv4OptionElement::Security {
classification,
protection_authority,
} => {
assert_eq!(*classification, 0x00AB);
assert_eq!(*protection_authority, 0x00CD);
assert!(opts[0].is_copied()); }
_ => panic!("Expected Security option"),
}
}
#[test]
fn test_ipv4_options_integration_multiple() {
let mut packet = create_test_packet();
packet[0] = 0x48;
packet.push(0x01);
packet.extend_from_slice(&[136, 0x04, 0x12, 0x34]);
packet.extend_from_slice(&[148, 0x04, 0x00, 0x00]);
packet.extend_from_slice(&[0x01, 0x01, 0x01]);
let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
assert_eq!(header_ext.raw_options.len(), 12);
assert!(header_ext.has_options());
let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(opts.len(), 6);
use crate::packet::ipv4::opt::Ipv4OptionElement;
assert!(matches!(opts[0], Ipv4OptionElement::Nop));
assert!(matches!(opts[1], Ipv4OptionElement::StreamId(0x1234)));
assert!(matches!(opts[2], Ipv4OptionElement::RouterAlert(0)));
assert!(matches!(opts[3], Ipv4OptionElement::Nop));
}
fn create_test_header() -> Ipv4Header {
Ipv4Header {
ver_ihl: 0x45, dscp_ecn: 0x00,
total_length: U16::new(20),
identification: U16::new(0x1234),
flags_frag_offset: U16::new(0x4000), ttl: 64,
protocol: IpProto::TCP,
checksum: U16::new(0),
src_ip: U32::new(0xC0A80164), dst_ip: U32::new(0x0A000001), }
}
fn create_test_packet() -> Vec<u8> {
let mut packet = Vec::new();
packet.push(0x45);
packet.push(0x00);
packet.extend_from_slice(&[0x00, 0x14]);
packet.extend_from_slice(&[0x12, 0x34]);
packet.extend_from_slice(&[0x40, 0x00]);
packet.push(64);
packet.push(6);
packet.extend_from_slice(&[0x00, 0x00]);
packet.extend_from_slice(&[192, 168, 1, 100]);
packet.extend_from_slice(&[10, 0, 0, 1]);
packet
}
}