diff options
| author | luwenpeng <[email protected]> | 2023-07-28 12:01:21 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-08-01 11:44:26 +0800 |
| commit | d9d9bd0e6343e319c93585fea4a6844e7b91088c (patch) | |
| tree | 215c31b68a8a2dd6ec3a1173fa375310a07a781a | |
| parent | 993825d24a9d87831043d7a8cf492cf00ab131e6 (diff) | |
[feature] Support IPv4 Decode
| -rw-r--r-- | src/protocol/ipv4.rs | 183 | ||||
| -rw-r--r-- | src/protocol/mod.rs | 3 |
2 files changed, 185 insertions, 1 deletions
diff --git a/src/protocol/ipv4.rs b/src/protocol/ipv4.rs new file mode 100644 index 0000000..3aaa182 --- /dev/null +++ b/src/protocol/ipv4.rs @@ -0,0 +1,183 @@ +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, Copy, 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 IPv4Header { + pub 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)?; + + 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::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"); + } + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 7932940..2fd8d7d 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,2 +1,3 @@ pub mod ethernet; -pub mod ip;
\ No newline at end of file +pub mod ip; +pub mod ipv4;
\ No newline at end of file |
