use crate::protocol::codec::Decode; 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 + * | | * + + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* * 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(Debug, PartialEq)] pub struct IPv6Extension { pub next_header: IPProtocol, pub ext_length: u8, // Extension total Length pub data: Vec, // Extension data length (ext_length - 2) } #[derive(Debug, PartialEq)] 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, pub extensions: Vec, } /****************************************************************************** * 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()))) } 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).map(|(i, l)| (i, l.to_vec()))?; Ok(( input, IPv6Extension { next_header, ext_length, data, }, )) } impl Decode for IPv6Header { type Iterm = IPv6Header; 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)?; 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(( remain, 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, extensions, }, )) } } /****************************************************************************** * TEST ******************************************************************************/ #[cfg(test)] mod tests { use super::IPv6Extension; use super::IPv6Header; use crate::protocol::codec::Decode; 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, ), extensions: Vec::new(), }; assert_eq!(IPv6Header::decode(&bytes), Ok((LAST_SLICE, expectation))); // example let result = IPv6Header::decode(&bytes); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // assert_eq!(1, 0); } #[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: */ 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::IPV6HOP, 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))); // example let result = IPv6Header::decode(&bytes); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // 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::IPV6ROUTING, 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); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // 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::IPV6HOP, 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::IPV6DEST, ext_length: 8, data: vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x00], }, IPv6Extension { next_header: IPProtocol::IPV6ROUTING, ext_length: 8, data: vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x00], }, IPv6Extension { next_header: IPProtocol::IPV6FRAGMENT, 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); match result { Ok((payload, header)) => { println!("OK: {:?}, payload: {}", header, payload.len()); } Err(e) => { println!("ERR: {:?}", e); } } // assert_eq!(1, 0); } }