1use alloc::vec;
4use alloc::vec::Vec;
5use core::net::Ipv6Addr;
6
7use crate::types::IpProtocol;
8use crate::util::{read_u16be, read_u32be, write_u16be, write_u32be, BuildError};
9
10pub const IPV6_HEADER_LEN: usize = 40;
12
13const MAX_EXTENSION_DEPTH: usize = 8;
15
16#[derive(Debug, Clone)]
18pub struct Ipv6Packet<'a> {
19 buf: &'a [u8],
20}
21
22impl<'a> Ipv6Packet<'a> {
23 pub fn new(buf: &'a [u8]) -> Option<Self> {
24 if buf.len() < IPV6_HEADER_LEN {
25 return None;
26 }
27 Some(Self { buf })
28 }
29
30 #[inline]
32 pub fn as_bytes(&self) -> &'a [u8] { self.buf }
33
34 #[inline]
35 pub fn version(&self) -> u8 {
36 self.buf[0] >> 4
37 }
38
39 #[inline]
40 pub fn traffic_class(&self) -> u8 {
41 ((self.buf[0] & 0x0F) << 4) | (self.buf[1] >> 4)
42 }
43
44 #[inline]
45 pub fn flow_label(&self) -> u32 {
46 read_u32be(&self.buf[..4]) & 0x000F_FFFF
47 }
48
49 #[inline]
50 pub fn payload_length(&self) -> u16 {
51 read_u16be(&self.buf[4..6])
52 }
53
54 #[inline]
55 pub fn next_header(&self) -> IpProtocol {
56 IpProtocol::from(self.buf[6])
57 }
58
59 #[inline]
60 pub fn hop_limit(&self) -> u8 {
61 self.buf[7]
62 }
63
64 #[inline]
65 pub fn source(&self) -> Ipv6Addr {
66 let b: [u8; 16] = self.buf[8..24].try_into().unwrap();
67 Ipv6Addr::from(b)
68 }
69
70 #[inline]
71 pub fn destination(&self) -> Ipv6Addr {
72 let b: [u8; 16] = self.buf[24..40].try_into().unwrap();
73 Ipv6Addr::from(b)
74 }
75
76 pub fn extension_headers(&self) -> Ipv6ExtensionHeadersIter<'a> {
78 Ipv6ExtensionHeadersIter {
79 next_header: self.next_header(),
80 data: &self.buf[IPV6_HEADER_LEN..],
81 depth: 0,
82 }
83 }
84
85 pub fn final_protocol(&self) -> IpProtocol {
87 let mut next = self.next_header();
88 let mut remaining = &self.buf[IPV6_HEADER_LEN..];
89 for _ in 0..MAX_EXTENSION_DEPTH {
90 if !is_extension_header(next) {
91 return next;
92 }
93 if remaining.len() < 2 {
94 break;
95 }
96 let hdr_len = extension_header_len(next, remaining);
97 if hdr_len == 0 {
98 break;
99 }
100 next = IpProtocol::from(remaining[0]);
101 if remaining.len() < hdr_len {
102 break;
103 }
104 remaining = &remaining[hdr_len..];
105 }
106 next
107 }
108
109 pub fn payload(&self) -> &'a [u8] {
111 let mut remaining = &self.buf[IPV6_HEADER_LEN..];
112 let mut next = self.next_header();
113 for _ in 0..MAX_EXTENSION_DEPTH {
114 if !is_extension_header(next) {
115 return remaining;
116 }
117 if remaining.len() < 2 {
118 return &[];
119 }
120 let hdr_len = extension_header_len(next, remaining);
121 if remaining.len() < hdr_len {
122 return &[];
123 }
124 next = IpProtocol::from(remaining[0]);
125 remaining = &remaining[hdr_len..];
126 }
127 remaining
128 }
129}
130
131fn is_extension_header(proto: IpProtocol) -> bool {
132 matches!(proto, IpProtocol::Unknown(0 | 43 | 44 | 60))
133}
134
135fn extension_header_len(proto: IpProtocol, data: &[u8]) -> usize {
136 match proto {
137 IpProtocol::Unknown(0 | 60) => (data[1] as usize + 1) * 8,
138 IpProtocol::Unknown(43) => (data[1] as usize + 1) * 8,
139 IpProtocol::Unknown(44) => 8,
140 _ => 0,
141 }
142}
143
144#[derive(Debug, Clone)]
146pub enum Ipv6ExtensionHeader<'a> {
147 HopByHop { next_header: IpProtocol, data: &'a [u8] },
148 Routing { next_header: IpProtocol, routing_type: u8, segments_left: u8, data: &'a [u8] },
149 Fragment { next_header: IpProtocol, fragment_offset: u16, more_fragments: bool, id: u32 },
150 Destination { next_header: IpProtocol, data: &'a [u8] },
151}
152
153pub struct Ipv6ExtensionHeadersIter<'a> {
155 next_header: IpProtocol,
156 data: &'a [u8],
157 depth: usize,
158}
159
160impl<'a> Iterator for Ipv6ExtensionHeadersIter<'a> {
161 type Item = Ipv6ExtensionHeader<'a>;
162
163 fn next(&mut self) -> Option<Self::Item> {
164 if self.depth >= MAX_EXTENSION_DEPTH || !is_extension_header(self.next_header) {
165 return None;
166 }
167 if self.data.len() < 2 {
168 return None;
169 }
170
171 let current_next = IpProtocol::from(self.data[0]);
172 let hdr_len = extension_header_len(self.next_header, self.data);
173 if self.data.len() < hdr_len || hdr_len == 0 {
174 return None;
175 }
176
177 let hdr_data = &self.data[..hdr_len];
178 let item = match self.next_header {
179 IpProtocol::Unknown(0) => Ipv6ExtensionHeader::HopByHop {
180 next_header: current_next,
181 data: hdr_data,
182 },
183 IpProtocol::Unknown(43) => Ipv6ExtensionHeader::Routing {
184 next_header: current_next,
185 routing_type: hdr_data[2],
186 segments_left: hdr_data[3],
187 data: hdr_data,
188 },
189 IpProtocol::Unknown(44) => {
190 let offset = read_u16be(&hdr_data[2..4]);
191 Ipv6ExtensionHeader::Fragment {
192 next_header: current_next,
193 fragment_offset: (offset >> 3) & 0x1FFF,
194 more_fragments: offset & 0x01 != 0,
195 id: read_u32be(&hdr_data[4..8]),
196 }
197 }
198 IpProtocol::Unknown(60) => Ipv6ExtensionHeader::Destination {
199 next_header: current_next,
200 data: hdr_data,
201 },
202 _ => return None,
203 };
204
205 self.next_header = current_next;
206 self.data = &self.data[hdr_len..];
207 self.depth += 1;
208
209 Some(item)
210 }
211}
212
213pub struct Ipv6PacketBuilder {
218 buf: Vec<u8>,
219 ext_headers: Vec<(IpProtocol, Vec<u8>)>,
220 payload: Option<Vec<u8>>,
221}
222
223impl Default for Ipv6PacketBuilder {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229impl Ipv6PacketBuilder {
230 pub fn new() -> Self {
231 let mut buf = vec![0u8; IPV6_HEADER_LEN];
232 buf[0] = 0x60; Self { buf, ext_headers: Vec::new(), payload: None }
234 }
235
236 pub fn traffic_class(mut self, tc: u8) -> Self {
237 self.buf[0] = (self.buf[0] & 0xF0) | (tc >> 4);
238 self.buf[1] = (tc & 0x0F) << 4 | (self.buf[1] & 0x0F);
239 self
240 }
241
242 pub fn flow_label(mut self, label: u32) -> Self {
243 let val = read_u32be(&self.buf[..4]);
244 let updated = (val & 0xFFF0_0000) | (label & 0x000F_FFFF);
245 write_u32be(&mut self.buf[..4], updated);
246 self
247 }
248
249 pub fn next_header(mut self, proto: IpProtocol) -> Self {
250 self.buf[6] = proto.into();
251 self
252 }
253
254 pub fn hop_limit(mut self, hl: u8) -> Self {
255 self.buf[7] = hl;
256 self
257 }
258
259 pub fn source(mut self, addr: Ipv6Addr) -> Self {
260 self.buf[8..24].copy_from_slice(&addr.octets());
261 self
262 }
263
264 pub fn destination(mut self, addr: Ipv6Addr) -> Self {
265 self.buf[24..40].copy_from_slice(&addr.octets());
266 self
267 }
268
269 pub fn add_extension_header(mut self, proto: IpProtocol, data: &[u8]) -> Self {
270 if proto == IpProtocol::Unknown(44) && data.len() != 8 {
272 return self;
273 }
274 if data.len() < 2 {
276 return self;
277 }
278 self.ext_headers.push((proto, data.to_vec()));
279 self
280 }
281
282 pub fn payload(mut self, data: &[u8]) -> Result<Self, BuildError> {
283 if data.len() > 65535 {
284 return Err(BuildError::PayloadTooLarge { max: 65535, actual: data.len() });
285 }
286 self.payload = Some(data.to_vec());
287 Ok(self)
288 }
289
290 pub fn build(mut self) -> Vec<u8> {
291 if self.ext_headers.len() > MAX_EXTENSION_DEPTH {
293 self.ext_headers.truncate(MAX_EXTENSION_DEPTH);
294 }
295
296 let final_proto: u8 = self.buf[6];
297
298 let mut total_ext_len = 0usize;
299 let mut ext_data = Vec::new();
300
301 if !self.ext_headers.is_empty() {
304 self.buf[6] = self.ext_headers[0].0.into();
306
307 for i in 0..self.ext_headers.len() {
308 let (_proto, ref data) = self.ext_headers[i];
309 let mut hdr = Vec::with_capacity(data.len());
310 hdr.push(if i + 1 < self.ext_headers.len() {
311 self.ext_headers[i + 1].0.into()
312 } else {
313 final_proto
314 });
315 hdr.extend_from_slice(&data[1..]); ext_data.extend_from_slice(&hdr);
317 total_ext_len += hdr.len();
318 }
319 }
320
321 let payload_len = total_ext_len + self.payload.as_ref().map_or(0, |p| p.len());
322 write_u16be(&mut self.buf[4..6], payload_len as u16);
323
324 let mut packet = self.buf;
325 packet.extend_from_slice(&ext_data);
326 if let Some(p) = self.payload {
327 packet.extend_from_slice(&p);
328 }
329 packet
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 const SAMPLE_IPV6: &[u8] = &[
339 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
348
349 #[test]
350 fn parse_ipv6() {
351 let pkt = Ipv6Packet::new(SAMPLE_IPV6).unwrap();
352 assert_eq!(pkt.version(), 6);
353 assert_eq!(pkt.next_header(), IpProtocol::Icmpv6);
354 assert_eq!(pkt.hop_limit(), 64);
355 assert_eq!(pkt.source(), Ipv6Addr::LOCALHOST);
356 assert_eq!(pkt.destination(), Ipv6Addr::LOCALHOST);
357 assert_eq!(pkt.payload_length(), 8);
358 }
359
360 #[test]
361 fn parse_too_short() {
362 assert!(Ipv6Packet::new(&[0u8; 39]).is_none());
363 assert!(Ipv6Packet::new(&[]).is_none());
364 }
365
366 #[test]
367 fn build_and_parse_roundtrip() {
368 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
369 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
370 let pkt_bytes = Ipv6PacketBuilder::new()
371 .source(src)
372 .destination(dst)
373 .hop_limit(128)
374 .next_header(IpProtocol::Tcp)
375 .payload(&[0xAA, 0xBB])
376 .unwrap()
377 .build();
378
379 let pkt = Ipv6Packet::new(&pkt_bytes).unwrap();
380 assert_eq!(pkt.source(), src);
381 assert_eq!(pkt.destination(), dst);
382 assert_eq!(pkt.hop_limit(), 128);
383 assert_eq!(pkt.next_header(), IpProtocol::Tcp);
384 assert_eq!(pkt.payload(), &[0xAA, 0xBB]);
385 }
386
387 #[test]
388 fn no_ext_headers() {
389 let pkt = Ipv6Packet::new(SAMPLE_IPV6).unwrap();
390 let headers: Vec<_> = pkt.extension_headers().collect();
391 assert!(headers.is_empty());
392 assert_eq!(pkt.final_protocol(), IpProtocol::Icmpv6);
393 }
394
395 #[test]
397 fn single_ext_header_hop_by_hop() {
398 let hbh_data: &[u8] = &[
400 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
401 ];
402 let payload = &[0xAA, 0xBB, 0xCC, 0xDD];
403 let src = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
404 let dst = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2);
405
406 let pkt_bytes = Ipv6PacketBuilder::new()
407 .source(src)
408 .destination(dst)
409 .hop_limit(64)
410 .next_header(IpProtocol::Icmpv6)
411 .add_extension_header(IpProtocol::Unknown(0), hbh_data)
412 .payload(payload)
413 .unwrap()
414 .build();
415
416 let pkt = Ipv6Packet::new(&pkt_bytes).unwrap();
417 assert_eq!(pkt.next_header(), IpProtocol::Unknown(0));
418 assert_eq!(pkt.final_protocol(), IpProtocol::Icmpv6);
419 assert_eq!(pkt.payload(), payload);
420
421 let ext: Vec<_> = pkt.extension_headers().collect();
422 assert_eq!(ext.len(), 1);
423 match &ext[0] {
424 Ipv6ExtensionHeader::HopByHop { next_header, .. } => {
425 assert_eq!(*next_header, IpProtocol::Icmpv6);
426 }
427 _ => panic!("expected HopByHop"),
428 }
429 }
430
431 #[test]
433 fn multi_ext_header_chain() {
434 let hbh: &[u8] = &[0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; let routing: &[u8] = &[0x2C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00]; let fragment: &[u8] = &[0x06, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x42]; let pkt_bytes = Ipv6PacketBuilder::new()
439 .source(Ipv6Addr::LOCALHOST)
440 .destination(Ipv6Addr::LOCALHOST)
441 .next_header(IpProtocol::Tcp)
442 .add_extension_header(IpProtocol::Unknown(0), hbh)
443 .add_extension_header(IpProtocol::Unknown(43), routing)
444 .add_extension_header(IpProtocol::Unknown(44), fragment)
445 .payload(&[0x01, 0x02])
446 .unwrap()
447 .build();
448
449 let pkt = Ipv6Packet::new(&pkt_bytes).unwrap();
450 assert_eq!(pkt.final_protocol(), IpProtocol::Tcp);
451
452 let ext: Vec<_> = pkt.extension_headers().collect();
453 assert_eq!(ext.len(), 3);
454 assert!(matches!(ext[0], Ipv6ExtensionHeader::HopByHop { .. }));
455 match &ext[1] {
456 Ipv6ExtensionHeader::Routing { routing_type, segments_left, .. } => {
457 assert_eq!(*routing_type, 0);
458 assert_eq!(*segments_left, 2);
459 }
460 _ => panic!("expected Routing"),
461 }
462 match &ext[2] {
463 Ipv6ExtensionHeader::Fragment { fragment_offset, more_fragments, id, .. } => {
464 assert_eq!(*fragment_offset, 1);
465 assert!(more_fragments);
466 assert_eq!(*id, 0x42);
467 }
468 _ => panic!("expected Fragment"),
469 }
470 }
471
472 #[test]
474 fn extension_header_depth_limit() {
475 let mut pkt_bytes = Ipv6PacketBuilder::new()
478 .source(Ipv6Addr::LOCALHOST)
479 .destination(Ipv6Addr::LOCALHOST)
480 .next_header(IpProtocol::Tcp);
481 for _ in 0..9 {
482 pkt_bytes = pkt_bytes.add_extension_header(
483 IpProtocol::Unknown(0),
484 &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
485 );
486 }
487 let pkt_bytes = pkt_bytes.payload(&[0x99]).unwrap().build();
488
489 let pkt = Ipv6Packet::new(&pkt_bytes).unwrap();
490 let count = pkt.extension_headers().count();
492 assert_eq!(count, 8, "iterator should stop at MAX_EXTENSION_DEPTH (8)");
493 }
494
495 #[test]
497 fn builder_rejects_invalid_ext_header_data() {
498 let pkt = Ipv6PacketBuilder::new()
500 .source(Ipv6Addr::LOCALHOST)
501 .destination(Ipv6Addr::LOCALHOST)
502 .next_header(IpProtocol::Tcp)
503 .add_extension_header(IpProtocol::Unknown(44), &[0x06, 0x00, 0x00]) .payload(&[0xAA])
505 .unwrap()
506 .build();
507 let parsed = Ipv6Packet::new(&pkt).unwrap();
508 assert_eq!(parsed.extension_headers().count(), 0);
509
510 let pkt = Ipv6PacketBuilder::new()
512 .source(Ipv6Addr::LOCALHOST)
513 .destination(Ipv6Addr::LOCALHOST)
514 .next_header(IpProtocol::Tcp)
515 .add_extension_header(IpProtocol::Unknown(0), &[0x06]) .payload(&[0xAA])
517 .unwrap()
518 .build();
519 let parsed = Ipv6Packet::new(&pkt).unwrap();
520 assert_eq!(parsed.extension_headers().count(), 0);
521 }
522
523 #[test]
525 fn builder_fragment_ext_header_roundtrip() {
526 let frag: &[u8] = &[0x06, 0x00, 0x00, 0x09, 0x12, 0x34, 0x56, 0x78];
527 let pkt = Ipv6PacketBuilder::new()
528 .source(Ipv6Addr::LOCALHOST)
529 .destination(Ipv6Addr::LOCALHOST)
530 .next_header(IpProtocol::Tcp)
531 .add_extension_header(IpProtocol::Unknown(44), frag)
532 .payload(&[0xCC])
533 .unwrap()
534 .build();
535 let parsed = Ipv6Packet::new(&pkt).unwrap();
536 let ext: Vec<_> = parsed.extension_headers().collect();
537 assert_eq!(ext.len(), 1);
538 match &ext[0] {
539 Ipv6ExtensionHeader::Fragment { fragment_offset, more_fragments, id, .. } => {
540 assert_eq!(*fragment_offset, 1);
541 assert!(more_fragments);
542 assert_eq!(*id, 0x12345678);
543 }
544 _ => panic!("expected Fragment"),
545 }
546 }
547
548 #[test]
550 fn parse_single_ext_header() {
551 let mut data = vec![
553 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
560 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
562 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
563 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
565 0xAA, 0xBB,
567 ];
568 data[4] = 0x00;
570 data[5] = 10;
571
572 let pkt = Ipv6Packet::new(&data).unwrap();
573 assert_eq!(pkt.next_header(), IpProtocol::Unknown(0));
574 assert_eq!(pkt.final_protocol(), IpProtocol::Icmpv6);
575 assert_eq!(pkt.payload(), &[0xAA, 0xBB]);
576
577 let ext: Vec<_> = pkt.extension_headers().collect();
578 assert_eq!(ext.len(), 1);
579 }
580}