diff options
| author | luwenpeng <[email protected]> | 2023-09-12 14:34:56 +0800 |
|---|---|---|
| committer | luwenpeng <[email protected]> | 2023-09-12 14:39:33 +0800 |
| commit | 8755e95f068da56c099e9c51dc4a89b55960e48d (patch) | |
| tree | 3db6cea2db66eb55d64a67dc214af5ed4fd18157 /src/protocol | |
| parent | 88172ebc1d79a7b289b125418ddc50f20421d944 (diff) | |
[feature] Support IPv6 Extensions Decode
Diffstat (limited to 'src/protocol')
| -rw-r--r-- | src/protocol/ethernet.rs | 2 | ||||
| -rw-r--r-- | src/protocol/ip.rs | 29 | ||||
| -rw-r--r-- | src/protocol/ipv4.rs | 2 | ||||
| -rw-r--r-- | src/protocol/ipv6.rs | 342 | ||||
| -rw-r--r-- | src/protocol/mpls.rs | 4 | ||||
| -rw-r--r-- | src/protocol/tcp.rs | 2 | ||||
| -rw-r--r-- | src/protocol/udp.rs | 2 | ||||
| -rw-r--r-- | src/protocol/vlan.rs | 2 |
8 files changed, 373 insertions, 12 deletions
diff --git a/src/protocol/ethernet.rs b/src/protocol/ethernet.rs index 6e25696..a588c0d 100644 --- a/src/protocol/ethernet.rs +++ b/src/protocol/ethernet.rs @@ -60,7 +60,7 @@ pub enum EtherType { Other(u16), } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct EthernetFrame { pub source_mac: MacAddress, pub dest_mac: MacAddress, diff --git a/src/protocol/ip.rs b/src/protocol/ip.rs index d7a1fe9..8d22e15 100644 --- a/src/protocol/ip.rs +++ b/src/protocol/ip.rs @@ -6,9 +6,10 @@ use nom::IResult; * Struct ******************************************************************************/ +#[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum IPProtocol { - HOPOPT, + IPV6_HOP_HDR, ICMP, IGMP, GGP, @@ -27,7 +28,12 @@ pub enum IPProtocol { CHAOS, UDP, IPV6, + IPV6_ROUTING_HDR, + IPV6_FRAGMENT_HDR, + ESP, + AUTH, ICMP6, + IPV6_DEST_HDR, Other(u8), } @@ -38,7 +44,7 @@ pub enum IPProtocol { impl From<u8> for IPProtocol { fn from(raw: u8) -> Self { match raw { - 0 => IPProtocol::HOPOPT, + 0 => IPProtocol::IPV6_HOP_HDR, // IPv6 Hop-by-Hop Options 1 => IPProtocol::ICMP, 2 => IPProtocol::IGMP, 3 => IPProtocol::GGP, @@ -57,12 +63,31 @@ impl From<u8> for IPProtocol { 16 => IPProtocol::CHAOS, 17 => IPProtocol::UDP, 41 => IPProtocol::IPV6, + 43 => IPProtocol::IPV6_ROUTING_HDR, // IPv6 Routing Header + 44 => IPProtocol::IPV6_FRAGMENT_HDR, // IPv6 Fragment Header + 50 => IPProtocol::ESP, // Encap Security Payload [RFC4303] + 51 => IPProtocol::AUTH, // Authentication Header [RFC4302] 58 => IPProtocol::ICMP6, + 60 => IPProtocol::IPV6_DEST_HDR, // IPv6 Destination Options other => IPProtocol::Other(other), } } } +impl IPProtocol { + pub fn is_ipv6_ext_header(next_header: IPProtocol) -> bool { + match next_header { + IPProtocol::IPV6_HOP_HDR => true, + IPProtocol::IPV6_ROUTING_HDR => true, + IPProtocol::IPV6_FRAGMENT_HDR => true, + // IPProtocol::ESP => true, + IPProtocol::AUTH => true, + IPProtocol::IPV6_DEST_HDR => true, + _ => false, + } + } +} + impl Decode for IPProtocol { type Iterm = IPProtocol; fn decode(input: &[u8]) -> IResult<&[u8], IPProtocol> { diff --git a/src/protocol/ipv4.rs b/src/protocol/ipv4.rs index f0ee008..61dce29 100644 --- a/src/protocol/ipv4.rs +++ b/src/protocol/ipv4.rs @@ -31,7 +31,7 @@ use std::net::Ipv4Addr; * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct IPv4Header { pub version: u8, // 4 bit pub ihl: u8, // 4 bit diff --git a/src/protocol/ipv6.rs b/src/protocol/ipv6.rs index f8bafdd..5a30847 100644 --- a/src/protocol/ipv6.rs +++ b/src/protocol/ipv6.rs @@ -41,7 +41,32 @@ use std::net::Ipv6Addr; * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/* + * IPv6 AH 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Next Header | Payload Len | RESERVED | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Security Parameters Index (SPI) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Sequence Number Field | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + Integrity Check Value-ICV (variable) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct IPv6Extension { + pub next_header: IPProtocol, + pub ext_length: u8, // Extension total Length + pub data: Vec<u8>, // Extension data length (ext_length - 2) +} + +#[derive(Clone, Debug, PartialEq, Eq)] pub struct IPv6Header { pub version: u8, // 4 bit pub dsc: u8, // Differentiated Services Codepoint: 6 bit @@ -52,6 +77,7 @@ pub struct IPv6Header { pub hop_limit: u8, pub source_address: Ipv6Addr, pub dest_address: Ipv6Addr, + pub extensions: Vec<IPv6Extension>, } /****************************************************************************** @@ -71,6 +97,34 @@ fn address_v6_decode(input: &[u8]) -> IResult<&[u8], Ipv6Addr> { Ok((input, Ipv6Addr::from(<[u8; 16]>::try_from(ipv6).unwrap()))) } +fn extension_decode(input: &[u8], curr_proto: IPProtocol) -> IResult<&[u8], IPv6Extension> { + let (input, next_header) = IPProtocol::decode(input)?; + let (input, mut ext_length) = number::streaming::be_u8(input)?; + + /* + * https://datatracker.ietf.org/doc/html/rfc4302#page-4 + * + * (Note that although IPv6 [DH98] characterizes AH as + * an extension header, its length is measured in 32-bit words, not the + * 64-bit words used by other IPv6 extension headers.) + */ + if curr_proto == IPProtocol::AUTH { + ext_length = ext_length * 4 + 8 // update Authentication Header length + } else { + ext_length = ext_length * 8 + 8; // update other extension header length + } + let (input, data) = bytes::streaming::take(ext_length - 2)(input)?; + + Ok(( + input, + IPv6Extension { + next_header, + ext_length, + data: data.to_vec(), + }, + )) +} + impl Decode for IPv6Header { type Iterm = IPv6Header; fn decode(input: &[u8]) -> IResult<&[u8], IPv6Header> { @@ -84,10 +138,18 @@ impl Decode for IPv6Header { let (input, source_address) = address_v6_decode(input)?; let (input, dest_address) = address_v6_decode(input)?; - // TODO IPv6 Ext Header Decode + let mut remain = input; + let mut next_proto = next_header; + let mut extensions = Vec::new(); + while IPProtocol::is_ipv6_ext_header(next_proto) { + let (left, extension) = extension_decode(remain, next_proto)?; + remain = left; + next_proto = extension.next_header; + extensions.push(extension); + } Ok(( - input, + remain, IPv6Header { version: ver_tc.0, dsc: (ver_tc.1 << 2) + ((tc_fl.0 & 0b1100) >> 2), @@ -98,6 +160,7 @@ impl Decode for IPv6Header { hop_limit, source_address, dest_address, + extensions, }, )) } @@ -109,6 +172,7 @@ impl Decode for IPv6Header { #[cfg(test)] mod tests { + use super::IPv6Extension; use super::IPv6Header; use crate::protocol::codec::Decode; use crate::protocol::ip::IPProtocol; @@ -160,6 +224,81 @@ mod tests { dest_address: Ipv6Addr::new( 0x2409, 0x8034, 0x4040, 0x5301, 0x0000, 0x0000, 0x0000, 0x0204, ), + extensions: Vec::new(), + }; + + 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"); + } + } + + #[test] + fn ipv6_hop_extension_decode() { + /* + * Internet Protocol Version 6, Src: ::, Dst: ff02::16 + * 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: 36 + * Next Header: IPv6 Hop-by-Hop Option (0) + * Hop Limit: 1 + * Source Address: :: + * Destination Address: ff02::16 + * IPv6 Hop-by-Hop Option + * Next Header: ICMPv6 (58) + * Length: 0 + * [Length: 8 bytes] + * Router Alert + * Type: Router Alert (0x05) + * 00.. .... = Action: Skip and continue (0) + * ..0. .... = May Change: No + * ...0 0101 = Low-Order Bits: 0x05 + * Length: 2 + * Router Alert: MLD (0) + * PadN + * Type: PadN (0x01) + * 00.. .... = Action: Skip and continue (0) + * ..0. .... = May Change: No + * ...0 0001 = Low-Order Bits: 0x01 + * Length: 0 + * PadN: <none> + */ + + let bytes = [ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x3a, 0x00, + 0x05, 0x02, 0x00, 0x00, 0x01, 0x00, /* Extensions Data */ + 0xff, /* Payload */ + ]; + + let expectation = IPv6Header { + version: 6, + dsc: 0, + ecn: 0, + flow_label: 0, + length: 36, + next_header: IPProtocol::IPV6_HOP_HDR, + hop_limit: 1, + source_address: Ipv6Addr::new( + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + ), + dest_address: Ipv6Addr::new( + 0xff02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0016, + ), + extensions: vec![IPv6Extension { + next_header: IPProtocol::ICMP6, + ext_length: 8, + data: vec![0x05, 0x02, 0x00, 0x00, 0x01, 0x00], + }], }; assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); @@ -171,5 +310,202 @@ mod tests { } else { println!("return: Incomplete data"); } + + // assert_eq!(1, 0); + } + + #[test] + fn ipv6_routing_extension_decode() { + /* + * Internet Protocol Version 6, Src: 2200::244:212:3fff:feae:22f7, Dst: 2200::240:2:0:0:4 + * 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: 32 + * Next Header: Routing Header for IPv6 (43) + * Hop Limit: 4 + * Source Address: 2200::244:212:3fff:feae:22f7 + * Destination Address: 2200::240:2:0:0:4 + * [Source SLAAC MAC: Dell_ae:22:f7 (00:12:3f:ae:22:f7)] + * Routing Header for IPv6 (Segment Routing) + * Next Header: ICMPv6 (58) + * Length: 2 + * [Length: 24 bytes] + * Type: Segment Routing (4) + * Segments Left: 1 + * Last Entry: 0 + * Flags: 0x00 + * Tag: 0000 + * Address[0]: 2200::210:2:0:0:4 + */ + + let bytes = [ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2b, 0x04, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x44, 0x02, 0x12, 0x3f, 0xff, 0xfe, 0xae, 0x22, 0xf7, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3a, 0x02, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* Extensions Data */ + 0xff, /* Payload */ + ]; + + let expectation = IPv6Header { + version: 6, + dsc: 0, + ecn: 0, + flow_label: 0, + length: 32, + next_header: IPProtocol::IPV6_ROUTING_HDR, + hop_limit: 4, + source_address: Ipv6Addr::new( + 0x2200, 0x0000, 0x0000, 0x0244, 0x0212, 0x3fff, 0xfeae, 0x22f7, + ), + dest_address: Ipv6Addr::new( + 0x2200, 0x0000, 0x0000, 0x0240, 0x0002, 0x0000, 0x0000, 0x0004, + ), + extensions: vec![IPv6Extension { + next_header: IPProtocol::ICMP6, + ext_length: 24, + data: vec![ + 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + ], + }], + }; + + 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"); + } + + // assert_eq!(1, 0); + } + + #[test] + fn ipv6_auth_extension_decode() { + // TODO need test + } + + #[test] + fn ipv6_mutil_extension_decode() { + /* + * Internet Protocol Version 6, Src: ::1, Dst: ::3 + * 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: 32 + * Next Header: IPv6 Hop-by-Hop Option (0) + * Hop Limit: 64 + * Source Address: ::1 + * Destination Address: ::3 + * IPv6 Hop-by-Hop Option + * Next Header: Destination Options for IPv6 (60) + * Length: 0 + * [Length: 8 bytes] + * PadN + * Type: PadN (0x01) + * 00.. .... = Action: Skip and continue (0) + * ..0. .... = May Change: No + * ...0 0001 = Low-Order Bits: 0x01 + * Length: 4 + * PadN: 00000000 + * Destination Options for IPv6 + * Next Header: Routing Header for IPv6 (43) + * Length: 0 + * [Length: 8 bytes] + * PadN + * Type: PadN (0x01) + * 00.. .... = Action: Skip and continue (0) + * ..0. .... = May Change: No + * ...0 0001 = Low-Order Bits: 0x01 + * Length: 4 + * PadN: 00000000 + * Routing Header for IPv6 (Source Route) + * Next Header: Fragment Header for IPv6 (44) + * Length: 0 + * [Length: 8 bytes] + * Type: Source Route (0) + * [Expert Info (Note/Deprecated): Routing header type is deprecated] + * [Routing header type is deprecated] + * [Severity level: Note] + * [Group: Deprecated] + * Segments Left: 0 + * Reserved: 00000000 + * Fragment Header for IPv6 + * Next header: No Next Header for IPv6 (59) + * Reserved octet: 0x00 + * 0000 0000 0000 0... = Offset: 0 (0 bytes) + * .... .... .... .00. = Reserved bits: 0 + * .... .... .... ...0 = More Fragments: No + * Identification: 0x00000000 + */ + + let bytes = [ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, /* Extensions Data */ + 0xff, /* Payload */ + ]; + + let expectation = IPv6Header { + version: 6, + dsc: 0, + ecn: 0, + flow_label: 0, + length: 32, + next_header: IPProtocol::IPV6_HOP_HDR, + hop_limit: 64, + source_address: Ipv6Addr::new( + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, + ), + dest_address: Ipv6Addr::new( + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, + ), + extensions: vec![ + IPv6Extension { + next_header: IPProtocol::IPV6_DEST_HDR, + ext_length: 8, + data: vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x00], + }, + IPv6Extension { + next_header: IPProtocol::IPV6_ROUTING_HDR, + ext_length: 8, + data: vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x00], + }, + IPv6Extension { + next_header: IPProtocol::IPV6_FRAGMENT_HDR, + ext_length: 8, + data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + IPv6Extension { + next_header: IPProtocol::Other(59), + ext_length: 8, + data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + ], + }; + + 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"); + } + + // assert_eq!(1, 0); } } diff --git a/src/protocol/mpls.rs b/src/protocol/mpls.rs index 2c4c443..e6a7a37 100644 --- a/src/protocol/mpls.rs +++ b/src/protocol/mpls.rs @@ -9,7 +9,7 @@ use nom::IResult; * Struct ******************************************************************************/ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct MplsHeader { pub label: u32, pub experimental: u8, @@ -17,7 +17,7 @@ pub struct MplsHeader { pub ttl: u8, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] // Ethernet pseudowire (PW) https://tools.ietf.org/html/rfc4448#section-3.1 pub struct PwEthHeader { pub control_word: u32, diff --git a/src/protocol/tcp.rs b/src/protocol/tcp.rs index a34fc37..5a88b25 100644 --- a/src/protocol/tcp.rs +++ b/src/protocol/tcp.rs @@ -69,7 +69,7 @@ pub enum TcpOption { }, } -#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct TcpHeader { pub source_port: u16, pub dest_port: u16, diff --git a/src/protocol/udp.rs b/src/protocol/udp.rs index 0eba88f..43cf75c 100644 --- a/src/protocol/udp.rs +++ b/src/protocol/udp.rs @@ -20,7 +20,7 @@ use nom::IResult; * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct UdpHeader { pub source_port: u16, pub dest_port: u16, diff --git a/src/protocol/vlan.rs b/src/protocol/vlan.rs index 7f64780..7533e41 100644 --- a/src/protocol/vlan.rs +++ b/src/protocol/vlan.rs @@ -9,7 +9,7 @@ use nom::IResult; * Struct ******************************************************************************/ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct VlanHeader { // A 3 bit number which refers to the IEEE 802.1p class of service and maps to the frame priority level. pub priority_code_point: u8, |
