1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct IPv6Header {
38 pub version: u8,
40 pub ds: u8,
44 pub ecn: u8,
48 pub flow_label: u32,
56 pub length: u16,
60 pub next_header: IPProtocol,
66 pub hop_limit: u8,
71 pub source_addr: Ipv6Addr,
73 pub dest_addr: Ipv6Addr,
75}
76
77pub enum Ipv6Atom {
79 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#[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}