summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2023-07-28 12:01:21 +0800
committerluwenpeng <[email protected]>2023-08-01 11:44:26 +0800
commitd9d9bd0e6343e319c93585fea4a6844e7b91088c (patch)
tree215c31b68a8a2dd6ec3a1173fa375310a07a781a
parent993825d24a9d87831043d7a8cf492cf00ab131e6 (diff)
[feature] Support IPv4 Decode
-rw-r--r--src/protocol/ipv4.rs183
-rw-r--r--src/protocol/mod.rs3
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