use crate::protocol::codec::Decode; use crate::protocol::ip::IPProtocol; use nom::bits; use nom::error::Error; use nom::number; use nom::sequence; use nom::IResult; use std::net::Ipv4Addr; /****************************************************************************** * Struct ******************************************************************************/ /* * Internet Header Format * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |Version| IHL |Type of Service| Total Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Identification |Flags| Fragment Offset | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Time to Live | Protocol | Header Checksum | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source Address | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Destination Address | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Options | Padding | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #[derive(Clone, Debug, PartialEq, Eq)] pub struct Ipv4Header { pub version: u8, // 4 bit pub ihl: u8, // 4 bit pub tos: u8, pub length: u16, pub id: u16, pub flags: u8, // 3 bit pub frag_offset: u16, // 13 bit pub ttl: u8, pub protocol: IPProtocol, pub checksum: u16, pub source_address: Ipv4Addr, pub dest_address: Ipv4Addr, } /****************************************************************************** * API ******************************************************************************/ fn flag_offset_decode(input: &[u8]) -> IResult<&[u8], (u8, u16)> { bits::bits::<_, _, Error<_>, _, _>(sequence::pair( bits::streaming::take(3u8), bits::streaming::take(13u16), ))(input) } fn version_hlen_decode(input: &[u8]) -> IResult<&[u8], (u8, u8)> { bits::bits::<_, _, Error<_>, _, _>(sequence::pair( bits::streaming::take(4u8), bits::streaming::take(4u8), ))(input) } fn address_v4_decode(input: &[u8]) -> IResult<&[u8], Ipv4Addr> { let (input, ipv4) = nom::bytes::streaming::take(4u8)(input)?; Ok((input, Ipv4Addr::from(<[u8; 4]>::try_from(ipv4).unwrap()))) } impl Decode for Ipv4Header { type Iterm = Ipv4Header; fn decode(input: &[u8]) -> IResult<&[u8], Ipv4Header> { let (input, verihl) = version_hlen_decode(input)?; let (input, tos) = number::streaming::be_u8(input)?; let (input, length) = number::streaming::be_u16(input)?; let (input, id) = number::streaming::be_u16(input)?; let (input, flag_frag_offset) = flag_offset_decode(input)?; let (input, ttl) = number::streaming::be_u8(input)?; let (input, protocol) = IPProtocol::decode(input)?; let (input, checksum) = number::streaming::be_u16(input)?; let (input, source_address) = address_v4_decode(input)?; let (input, dest_address) = address_v4_decode(input)?; // TODO IPv4 Options Decode Ok(( input, Ipv4Header { version: verihl.0, ihl: verihl.1 * 4, // verihl.1 * 32 / 8 tos, length, id, flags: flag_frag_offset.0, frag_offset: flag_frag_offset.1, ttl, protocol, checksum, source_address, dest_address, }, )) } } /****************************************************************************** * TEST ******************************************************************************/ #[cfg(test)] mod tests { use super::Ipv4Header; use crate::protocol::codec::Decode; use crate::protocol::ip::IPProtocol; use std::net::Ipv4Addr; const LAST_SLICE: &'static [u8] = &[0xff]; #[test] fn ipv4_header_decode() { /* * Internet Protocol Version 4, Src: 192.168.0.101, Dst: 121.14.154.93 * 0100 .... = Version: 4 * .... 0101 = Header Length: 20 bytes (5) * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) * 0000 00.. = Differentiated Services Codepoint: Default (0) * .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0) * Total Length: 70 * Identification: 0xe2db (58075) * 000. .... = Flags: 0x0 * 0... .... = Reserved bit: Not set * .0.. .... = Don't fragment: Not set * ..0. .... = More fragments: Not set * ...0 0000 0000 0000 = Fragment Offset: 0 * Time to Live: 64 * Protocol: UDP (17) * Header Checksum: 0xc352 [correct] * [Header checksum status: Good] * [Calculated Checksum: 0xc352] * Source Address: 192.168.0.101 * Destination Address: 121.14.154.93 * User Datagram Protocol, Src Port: 64820, Dst Port: 53 */ let bytes = [ 0x45, /* Version and Header length */ 0x00, /* Differentiated Services Field */ 0x00, 0x46, /* Total Length */ 0xe2, 0xdb, /* Identification */ 0x00, 0x00, /* Flags and Fragment Offset */ 0x40, /* Time to Live */ 0x11, /* Protocol */ 0xc3, 0x52, /* Header Checksum */ 0xc0, 0xa8, 0x00, 0x65, /* Source Address */ 0x79, 0x0e, 0x9a, 0x5d, /* Destination Address */ 0xff, /* Payload */ ]; let expectation = Ipv4Header { version: 4, ihl: 20, tos: 0, length: 70, id: 0xe2db, flags: 0x0, frag_offset: 0, ttl: 64, protocol: IPProtocol::UDP, checksum: 0xc352, source_address: Ipv4Addr::new(192, 168, 0, 101), dest_address: Ipv4Addr::new(121, 14, 154, 93), }; assert_eq!(Ipv4Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example let result = Ipv4Header::decode(&bytes); if let Ok((payload, header)) = result { println!("return: {:?}, payload: {}", header, payload.len()); } else { println!("return: Incomplete data"); } } }