From 9dd56a85f858f73fbc03079349f4c4b9b3295b79 Mon Sep 17 00:00:00 2001 From: luwenpeng Date: Fri, 28 Jul 2023 12:14:01 +0800 Subject: [feature] Support IPv6 Decode --- src/protocol/ipv6.rs | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/protocol/mod.rs | 3 +- 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/protocol/ipv6.rs diff --git a/src/protocol/ipv6.rs b/src/protocol/ipv6.rs new file mode 100644 index 0000000..283172b --- /dev/null +++ b/src/protocol/ipv6.rs @@ -0,0 +1,170 @@ +use crate::protocol::ip::IPProtocol; +use nom::bits; +use nom::bytes; +use nom::error::Error; +use nom::number; +use nom::sequence; +use nom::IResult; +use std::convert::TryFrom; +use std::net::Ipv6Addr; + +/****************************************************************************** + * Struct + ******************************************************************************/ + +/* + * IPv6 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| Traffic Class | Flow Label | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Payload Length | Next Header | Hop Limit | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Source Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Destination Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct IPv6Header { + pub version: u8, // 4 bit + pub dsc: u8, // Differentiated Services Codepoint: 6 bit + pub ecn: u8, // Explicit Congestion Notification: 2 bit + pub flow_label: u32, // 20 bit + pub length: u16, + pub next_header: IPProtocol, + pub hop_limit: u8, + pub source_address: Ipv6Addr, + pub dest_address: Ipv6Addr, +} + +/****************************************************************************** + * API + ******************************************************************************/ + +fn half_byte_decode(input: &[u8]) -> IResult<&[u8], (u8, u8)> { + bits::bits::<_, _, Error<_>, _, _>(sequence::pair( + bits::streaming::take(4u8), + bits::streaming::take(4u8), + ))(input) +} + +fn address_v6_decode(input: &[u8]) -> IResult<&[u8], Ipv6Addr> { + let (input, ipv6) = bytes::streaming::take(16u8)(input)?; + + Ok((input, Ipv6Addr::from(<[u8; 16]>::try_from(ipv6).unwrap()))) +} + +impl IPv6Header { + pub fn decode(input: &[u8]) -> IResult<&[u8], IPv6Header> { + let (input, ver_tc) = half_byte_decode(input)?; + let (input, tc_fl) = half_byte_decode(input)?; + let (input, fl): (_, u32) = + bits::bits::<_, _, Error<_>, _, _>(bits::streaming::take(16u8))(input)?; + let (input, length) = number::streaming::be_u16(input)?; + let (input, next_header) = IPProtocol::decode(input)?; + let (input, hop_limit) = number::streaming::be_u8(input)?; + let (input, source_address) = address_v6_decode(input)?; + let (input, dest_address) = address_v6_decode(input)?; + + Ok(( + input, + IPv6Header { + version: ver_tc.0, + dsc: (ver_tc.1 << 2) + ((tc_fl.0 & 0b1100) >> 2), + ecn: tc_fl.0 & 0b11, + flow_label: (u32::from(tc_fl.1) << 16) + fl, + length, + next_header, + hop_limit, + source_address, + dest_address, + }, + )) + } +} + +/****************************************************************************** + * TEST + ******************************************************************************/ + +#[cfg(test)] +mod tests { + use super::IPv6Header; + use crate::protocol::ip::IPProtocol; + use std::net::Ipv6Addr; + + const LAST_SLICE: &'static [u8] = &[0xff]; + + #[test] + fn ipv6_header_decode() { + /* + * Internet Protocol Version 6, Src: 2409:8034:4025::50:a31, Dst: 2409:8034:4040:5301::204 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0) + * .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 1416 + * Next Header: UDP (17) + * Hop Limit: 252 + * Source Address: 2409:8034:4025::50:a31 + * Destination Address: 2409:8034:4040:5301::204 + */ + + let bytes = [ + 0x60, /* Version and Partial Differentiated Services Codepoint */ + 0x00, /* Partial Differentiated Services Codepoint and Explicit Congestion Notification and Partial Flow Label */ + 0x00, 0x00, /* Partial Flow Label */ + 0x05, 0x88, /* Payload Length */ + 0x11, /* Next Header */ + 0xfc, /* Hop Limit */ + 0x24, 0x09, 0x80, 0x34, 0x40, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x0a, 0x31, /* Source Address */ + 0x24, 0x09, 0x80, 0x34, 0x40, 0x40, 0x53, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x04, /* Destination Address */ + 0xff, /* Payload */ + ]; + + let expectation = IPv6Header { + version: 6, + dsc: 0, + ecn: 0, + flow_label: 0, + length: 1416, + next_header: IPProtocol::UDP, + hop_limit: 252, + source_address: Ipv6Addr::new( + 0x2409, 0x8034, 0x4025, 0x0000, 0x0000, 0x0000, 0x0050, 0x0a31, + ), + dest_address: Ipv6Addr::new( + 0x2409, 0x8034, 0x4040, 0x5301, 0x0000, 0x0000, 0x0000, 0x0204, + ), + }; + + assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); + + // example + let result = IPv6Header::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 2fd8d7d..7a3bb05 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,3 +1,4 @@ pub mod ethernet; pub mod ip; -pub mod ipv4; \ No newline at end of file +pub mod ipv4; +pub mod ipv6; \ No newline at end of file -- cgit v1.2.3