binator_network/
ipv6.rs

1//! Handles parsing of IPv6 headers
2
3use std::{
4  fmt::{
5    Display,
6    Formatter,
7  },
8  net::Ipv6Addr,
9};
10
11use binator::{
12  base::{
13    nbit,
14    octet,
15    NBit,
16  },
17  utils::{
18    Utils,
19    UtilsAtom,
20  },
21  Contexting,
22  CoreAtom,
23  Parse,
24  Parsed,
25  Streaming,
26  Success,
27};
28
29use crate::ip_protocol::{
30  self,
31  IPProtocol,
32};
33
34/// <https://en.wikipedia.org/wiki/IPv6_packet>
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct IPv6Header {
38  /// The constant 6 (bit sequence 0110).
39  pub version: u8,
40  /// Differentiated services is a computer networking architecture
41  /// that specifies a mechanism for classifying and managing network traffic
42  /// and providing quality of service (QoS) on modern IP networks.
43  pub ds: u8,
44  /// Explicit Congestion Notification (ECN); priority values subdivide into
45  /// ranges: traffic where the source provides congestion control and
46  /// non-congestion control traffic.
47  pub ecn: u8,
48  /// A high-entropy identifier of a flow of packets between a source and
49  /// destination. A flow is a group of packets, e.g., a TCP session or a media
50  /// stream. The special flow label 0 means the packet does not belong to any
51  /// flow (using this scheme). An older scheme identifies flow by source
52  /// address and port, destination address and port, protocol (value of the
53  /// last Next Header field). It has further been suggested that the flow
54  /// label be used to help detect spoofed packets.
55  pub flow_label: u32,
56  /// The size of the payload in octets, including any extension headers. The
57  /// length is set to zero when a Hop-by-Hop extension header carries a Jumbo
58  /// Payload option.
59  pub length: u16,
60  /// Specifies the type of the next header. This field usually specifies the
61  /// transport layer protocol used by a packet's payload. When extension
62  /// headers are present in the packet this field indicates which extension
63  /// header follows. The values are shared with those used for the IPv4
64  /// protocol field, as both fields have the same function.
65  pub next_header: IPProtocol,
66  /// Replaces the time to live field in IPv4. This value is decremented by one
67  /// at each forwarding node and the packet is discarded if it becomes 0.
68  /// However, the destination node should process the packet normally even if
69  /// received with a hop limit of 0.
70  pub hop_limit: u8,
71  /// The unicast IPv6 address of the sending node.
72  pub source_addr: Ipv6Addr,
73  /// The IPv6 unicast or multicast address of the destination node(s).
74  pub dest_addr: Ipv6Addr,
75}
76
77/// Aom produced by ipv6_header parser
78pub enum Ipv6Atom {
79  /// When version is not 6
80  Version(u8),
81}
82
83impl Display for Ipv6Atom {
84  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85    match self {
86      Self::Version(version) => {
87        write!(f, "Ipv4Context: Version field is not 6 found {}", version)
88      }
89    }
90  }
91}
92
93/// Parse IPv6 header
94#[cfg_attr(
95  feature = "tracing",
96  tracing::instrument(level = "trace", skip_all, ret(Display))
97)]
98pub fn ipv6_header<Stream, Context>(stream: Stream) -> Parsed<IPv6Header, Stream, Context>
99where
100  Stream: Clone,
101  Stream: Eq,
102  Stream: Streaming,
103  Stream::Item: Into<u8>,
104  Context: Contexting<CoreAtom<Stream>>,
105  Context: Contexting<UtilsAtom<Stream>>,
106  Context: Contexting<UtilsAtom<Stream>>,
107  Context: Contexting<Ipv6Atom>,
108{
109  let Success {
110    token: (version, tc_0),
111    stream,
112  } = nbit(NBit::FOUR)
113    .try_map(|(version, tc_0)| {
114      if version == 6 {
115        Ok((version, tc_0))
116      } else {
117        Err(Context::new(Ipv6Atom::Version(version)))
118      }
119    })
120    .parse(stream)?;
121
122  let Success {
123    token: (tc_1, flow_label_0),
124    stream,
125  } = nbit(NBit::FOUR).parse(stream)?;
126
127  let Success {
128    token: flow_label,
129    stream,
130  } = octet
131    .and(octet)
132    .map(|(flow_label_1, flow_label_2)| {
133      u32::from_be_bytes([0, flow_label_0, flow_label_1, flow_label_2])
134    })
135    .parse(stream)?;
136
137  let Success {
138    token: length,
139    stream,
140  } = octet.fill().map(u16::from_be_bytes).parse(stream)?;
141  let Success {
142    token: next_header,
143    stream,
144  } = ip_protocol::ip_protocol.parse(stream)?;
145
146  let Success {
147    token: hop_limit,
148    stream,
149  } = octet.parse(stream)?;
150
151  let Success {
152    token: source_addr,
153    stream,
154  } = octet.fill().map(Ipv6Addr::from).parse(stream)?;
155
156  let Success {
157    token: dest_addr,
158    stream,
159  } = octet.fill().map(Ipv6Addr::from).parse(stream)?;
160
161  Parsed::Success {
162    token: IPv6Header {
163      version,
164      ds: (tc_0 << 2) + (tc_1 >> 2),
165      ecn: tc_1 & 0b11,
166      flow_label,
167      length,
168      next_header,
169      hop_limit,
170      source_addr,
171      dest_addr,
172    },
173    stream,
174  }
175}
176
177#[cfg(test)]
178mod tests {
179  use std::net::Ipv6Addr;
180
181  use binator::{
182    context::Ignore,
183    Parsed,
184  };
185  use pretty_assertions::assert_eq;
186
187  use super::{
188    IPProtocol,
189    IPv6Header,
190  };
191
192  #[test]
193  fn ipv6_header() {
194    let bytes = [
195      0x60, 0x20, 0x01, 0xFF, 0x05, 0x78, 0x3A, 0x05, 0x20, 0x01, 0x0D, 0xB8, 0x5C, 0xF8, 0x1A,
196      0xA8, 0x24, 0x81, 0x61, 0xE6, 0x5A, 0xC6, 0x03, 0xE0, 0x20, 0x01, 0x0D, 0xB8, 0x78, 0x90,
197      0x2A, 0xE9, 0x90, 0x8F, 0xA9, 0xF4, 0x2F, 0x4A, 0x9B, 0x80,
198    ];
199
200    let expectation = IPv6Header {
201      version: 6,
202      ds: 0,
203      ecn: 2,
204      flow_label: 511,
205      length: 1400,
206      next_header: IPProtocol::ICMP_6,
207      hop_limit: 5,
208      source_addr: Ipv6Addr::new(0x2001, 0xDB8, 0x5CF8, 0x1AA8, 0x2481, 0x61E6, 0x5AC6, 0x3E0),
209      dest_addr: Ipv6Addr::new(
210        0x2001, 0xDB8, 0x7890, 0x2AE9, 0x908F, 0xA9F4, 0x2F4A, 0x9B80,
211      ),
212    };
213    assert_eq!(
214      super::ipv6_header::<_, Ignore>(&bytes[..]),
215      Parsed::Success {
216        token: expectation,
217        stream: "".as_bytes(),
218      }
219    );
220  }
221}