summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2022-12-22 18:33:27 +0800
committerluwenpeng <[email protected]>2022-12-22 18:33:27 +0800
commitbf49a73f71e6fc517ff31a6cbe50eb7f87392027 (patch)
tree0ea05dfb531e19a483c3efe83f6ff942ad9403da
parentf1d20c6096ef6533c4388284513cfa959932458f (diff)
TSG-12767 使用RUST解析IPv4 Header
-rw-r--r--src/protocol/ipv4.rs171
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)));
+ }
+}