summaryrefslogtreecommitdiff
path: root/src/protocol/ipv6.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol/ipv6.rs')
-rw-r--r--src/protocol/ipv6.rs342
1 files changed, 339 insertions, 3 deletions
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);
}
}