diff options
| author | luwenpeng <[email protected]> | 2022-12-22 18:33:27 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2022-12-22 18:33:27 +0800 |
| commit | bf49a73f71e6fc517ff31a6cbe50eb7f87392027 (patch) | |
| tree | 0ea05dfb531e19a483c3efe83f6ff942ad9403da | |
| parent | f1d20c6096ef6533c4388284513cfa959932458f (diff) | |
TSG-12767 使用RUST解析IPv4 Header
| -rw-r--r-- | src/protocol/ipv4.rs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/protocol/ipv4.rs b/src/protocol/ipv4.rs new file mode 100644 index 0000000..79e7d97 --- /dev/null +++ b/src/protocol/ipv4.rs @@ -0,0 +1,171 @@ +use crate::protocol::ip::{self, 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 src_addr: Ipv4Addr, + pub dst_addr: Ipv4Addr, +} + +/****************************************************************************** + * Parse + ******************************************************************************/ + +fn parse_flag_and_frag_offset(input: &[u8]) -> IResult<&[u8], (u8, u16)> { + bits::bits::<_, _, Error<_>, _, _>(sequence::pair( + bits::streaming::take(3u8), + bits::streaming::take(13u16), + ))(input) +} + +fn parse_version_and_header_length(input: &[u8]) -> IResult<&[u8], (u8, u8)> { + bits::bits::<_, _, Error<_>, _, _>(sequence::pair( + bits::streaming::take(4u8), + bits::streaming::take(4u8), + ))(input) +} + +fn parse_ipv4_address(input: &[u8]) -> IResult<&[u8], Ipv4Addr> { + let (input, ipv4) = nom::bytes::streaming::take(4u8)(input)?; + + Ok((input, Ipv4Addr::from(<[u8; 4]>::try_from(ipv4).unwrap()))) +} + +pub fn parse_ipv4(input: &[u8]) -> IResult<&[u8], IPv4Header> { + let (input, verihl) = parse_version_and_header_length(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) = parse_flag_and_frag_offset(input)?; + let (input, ttl) = number::streaming::be_u8(input)?; + let (input, protocol) = ip::protocol(input)?; + let (input, checksum) = number::streaming::be_u16(input)?; + let (input, src_addr) = parse_ipv4_address(input)?; + let (input, dst_addr) = parse_ipv4_address(input)?; + + Ok(( + input, + IPv4Header { + version: verihl.0, + ihl: verihl.1, + tos, + length, + id, + flags: flag_frag_offset.0, + frag_offset: flag_frag_offset.1, + ttl, + protocol, + checksum, + src_addr, + dst_addr, + }, + )) +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::parse_ipv4; + use super::IPv4Header; + use crate::ip::IPProtocol; + use std::net::Ipv4Addr; + + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + fn parse_ipv4_works() { + /* + * Internet Protocol Version 4, Src: 192.168.0.100, Dst: 14.215.177.38 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 64 + * Identification: 0x0000 (0) + * Flags: 0x40, Don't fragment + * 0... .... = Reserved bit: Not set + * .1.. .... = Don't fragment: Set + * ..0. .... = More fragments: Not set + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: TCP (6) + * Header Checksum: 0xb9ae [correct] + * [Header checksum status: Good] + * [Calculated Checksum: 0xb9ae] + * Source Address: 192.168.0.100 + * Destination Address: 14.215.177.38 + */ + + let bytes = [ + 0x45, /* Version and Header length */ + 0x00, /* Differentiated Services Field */ + 0x01, 0xda, /* Total Length */ + 0x00, 0x00, /* Identification */ + 0x40, 0x00, /* Flags and Fragment Offset */ + 0x40, /* Time to Live */ + 0x06, /* Protocol */ + 0xb8, 0x14, /* Header Checksum */ + 0xc0, 0xa8, 0x00, 0x64, /* Source Address */ + 0x0e, 0xd7, 0xb1, 0x26, /* Destination Address */ + 0xff, /* Payload */ + ]; + + let expectation = IPv4Header { + version: 4, + ihl: 5, + tos: 0, + length: 474, + id: 0x0000, + flags: 0x2, + frag_offset: 0, + ttl: 64, + protocol: IPProtocol::TCP, + checksum: 0xb814, + src_addr: Ipv4Addr::new(192, 168, 0, 100), + dst_addr: Ipv4Addr::new(14, 215, 177, 38), + }; + + assert_eq!(parse_ipv4(&bytes), Ok((LAST_SLICE, expectation))); + } +} |
