1use super::{Error, Result};
2#[cfg(feature = "proto-rpl")]
3use super::{RplHopByHopPacket, RplHopByHopRepr};
4
5use byteorder::{ByteOrder, NetworkEndian};
6use core::fmt;
7
8enum_with_unknown! {
9 pub enum Type(u8) {
11 Pad1 = 0,
13 PadN = 1,
15 RouterAlert = 5,
17 Rpl = 0x63,
19 }
20}
21
22impl fmt::Display for Type {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match *self {
25 Type::Pad1 => write!(f, "Pad1"),
26 Type::PadN => write!(f, "PadN"),
27 Type::Rpl => write!(f, "RPL"),
28 Type::RouterAlert => write!(f, "RouterAlert"),
29 Type::Unknown(id) => write!(f, "{id}"),
30 }
31 }
32}
33
34enum_with_unknown! {
35 pub enum RouterAlert(u16) {
41 MulticastListenerDiscovery = 0,
42 Rsvp = 1,
43 ActiveNetworks = 2,
44 }
45}
46
47impl RouterAlert {
48 pub const DATA_LEN: u8 = 2;
52}
53
54#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub enum FailureType {
59 Skip = 0b00000000,
61 Discard = 0b01000000,
63 DiscardSendAll = 0b10000000,
65 DiscardSendUnicast = 0b11000000,
68}
69
70impl From<u8> for FailureType {
71 fn from(value: u8) -> FailureType {
72 match value & 0b11000000 {
73 0b00000000 => FailureType::Skip,
74 0b01000000 => FailureType::Discard,
75 0b10000000 => FailureType::DiscardSendAll,
76 0b11000000 => FailureType::DiscardSendUnicast,
77 _ => unreachable!(),
78 }
79 }
80}
81
82impl From<FailureType> for u8 {
83 fn from(value: FailureType) -> Self {
84 match value {
85 FailureType::Skip => 0b00000000,
86 FailureType::Discard => 0b01000000,
87 FailureType::DiscardSendAll => 0b10000000,
88 FailureType::DiscardSendUnicast => 0b11000000,
89 }
90 }
91}
92
93impl fmt::Display for FailureType {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 match *self {
96 FailureType::Skip => write!(f, "skip"),
97 FailureType::Discard => write!(f, "discard"),
98 FailureType::DiscardSendAll => write!(f, "discard and send error"),
99 FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"),
100 }
101 }
102}
103
104impl From<Type> for FailureType {
105 fn from(other: Type) -> FailureType {
106 let raw: u8 = other.into();
107 Self::from(raw & 0b11000000u8)
108 }
109}
110
111#[derive(Debug, PartialEq, Eq)]
113#[cfg_attr(feature = "defmt", derive(defmt::Format))]
114pub struct Ipv6Option<T: AsRef<[u8]>> {
115 buffer: T,
116}
117
118mod field {
127 #![allow(non_snake_case)]
128
129 use crate::wire::field::*;
130
131 pub const TYPE: usize = 0;
133 pub const LENGTH: usize = 1;
135 pub const fn DATA(length: u8) -> Field {
137 2..length as usize + 2
138 }
139}
140
141impl<T: AsRef<[u8]>> Ipv6Option<T> {
142 pub const fn new_unchecked(buffer: T) -> Ipv6Option<T> {
144 Ipv6Option { buffer }
145 }
146
147 pub fn new_checked(buffer: T) -> Result<Ipv6Option<T>> {
152 let opt = Self::new_unchecked(buffer);
153 opt.check_len()?;
154 Ok(opt)
155 }
156
157 pub fn check_len(&self) -> Result<()> {
164 let data = self.buffer.as_ref();
165 let len = data.len();
166
167 if len < field::LENGTH {
168 return Err(Error);
169 }
170
171 if self.option_type() == Type::Pad1 {
172 return Ok(());
173 }
174
175 if len == field::LENGTH {
176 return Err(Error);
177 }
178
179 let df = field::DATA(data[field::LENGTH]);
180
181 if len < df.end {
182 return Err(Error);
183 }
184
185 Ok(())
186 }
187
188 pub fn into_inner(self) -> T {
190 self.buffer
191 }
192
193 #[inline]
195 pub fn option_type(&self) -> Type {
196 let data = self.buffer.as_ref();
197 Type::from(data[field::TYPE])
198 }
199
200 #[inline]
205 pub fn data_len(&self) -> u8 {
206 let data = self.buffer.as_ref();
207 data[field::LENGTH]
208 }
209}
210
211impl<'a, T: AsRef<[u8]> + ?Sized> Ipv6Option<&'a T> {
212 #[inline]
217 pub fn data(&self) -> &'a [u8] {
218 let len = self.data_len();
219 let data = self.buffer.as_ref();
220 &data[field::DATA(len)]
221 }
222}
223
224impl<T: AsRef<[u8]> + AsMut<[u8]>> Ipv6Option<T> {
225 #[inline]
227 pub fn set_option_type(&mut self, value: Type) {
228 let data = self.buffer.as_mut();
229 data[field::TYPE] = value.into();
230 }
231
232 #[inline]
237 pub fn set_data_len(&mut self, value: u8) {
238 let data = self.buffer.as_mut();
239 data[field::LENGTH] = value;
240 }
241}
242
243impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Ipv6Option<&'a mut T> {
244 #[inline]
249 pub fn data_mut(&mut self) -> &mut [u8] {
250 let len = self.data_len();
251 let data = self.buffer.as_mut();
252 &mut data[field::DATA(len)]
253 }
254}
255
256impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 match Repr::parse(self) {
259 Ok(repr) => write!(f, "{repr}"),
260 Err(err) => {
261 write!(f, "IPv6 Extension Option ({err})")?;
262 Ok(())
263 }
264 }
265 }
266}
267
268#[derive(Debug, PartialEq, Eq, Clone, Copy)]
270#[cfg_attr(feature = "defmt", derive(defmt::Format))]
271#[non_exhaustive]
272pub enum Repr<'a> {
273 Pad1,
274 PadN(u8),
275 RouterAlert(RouterAlert),
276 #[cfg(feature = "proto-rpl")]
277 Rpl(RplHopByHopRepr),
278 Unknown {
279 type_: Type,
280 length: u8,
281 data: &'a [u8],
282 },
283}
284
285impl<'a> Repr<'a> {
286 pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>>
288 where
289 T: AsRef<[u8]> + ?Sized,
290 {
291 opt.check_len()?;
292 match opt.option_type() {
293 Type::Pad1 => Ok(Repr::Pad1),
294 Type::PadN => Ok(Repr::PadN(opt.data_len())),
295 Type::RouterAlert => {
296 if opt.data_len() == RouterAlert::DATA_LEN {
297 let raw = NetworkEndian::read_u16(opt.data());
298 Ok(Repr::RouterAlert(RouterAlert::from(raw)))
299 } else {
300 Err(Error)
301 }
302 }
303 #[cfg(feature = "proto-rpl")]
304 Type::Rpl => Ok(Repr::Rpl(RplHopByHopRepr::parse(
305 &RplHopByHopPacket::new_checked(opt.data())?,
306 ))),
307 #[cfg(not(feature = "proto-rpl"))]
308 Type::Rpl => Ok(Repr::Unknown {
309 type_: Type::Rpl,
310 length: opt.data_len(),
311 data: opt.data(),
312 }),
313
314 unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown {
315 type_: unknown_type,
316 length: opt.data_len(),
317 data: opt.data(),
318 }),
319 }
320 }
321
322 pub const fn buffer_len(&self) -> usize {
324 match *self {
325 Repr::Pad1 => 1,
326 Repr::PadN(length) => field::DATA(length).end,
327 Repr::RouterAlert(_) => field::DATA(RouterAlert::DATA_LEN).end,
328 #[cfg(feature = "proto-rpl")]
329 Repr::Rpl(opt) => field::DATA(opt.buffer_len() as u8).end,
330 Repr::Unknown { length, .. } => field::DATA(length).end,
331 }
332 }
333
334 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) {
336 match *self {
337 Repr::Pad1 => opt.set_option_type(Type::Pad1),
338 Repr::PadN(len) => {
339 opt.set_option_type(Type::PadN);
340 opt.set_data_len(len);
341 for x in opt.data_mut().iter_mut() {
343 *x = 0
344 }
345 }
346 Repr::RouterAlert(router_alert) => {
347 opt.set_option_type(Type::RouterAlert);
348 opt.set_data_len(RouterAlert::DATA_LEN);
349 NetworkEndian::write_u16(opt.data_mut(), router_alert.into());
350 }
351 #[cfg(feature = "proto-rpl")]
352 Repr::Rpl(rpl) => {
353 opt.set_option_type(Type::Rpl);
354 opt.set_data_len(4);
355 rpl.emit(&mut crate::wire::RplHopByHopPacket::new_unchecked(
356 opt.data_mut(),
357 ));
358 }
359 Repr::Unknown {
360 type_,
361 length,
362 data,
363 } => {
364 opt.set_option_type(type_);
365 opt.set_data_len(length);
366 opt.data_mut().copy_from_slice(&data[..length as usize]);
367 }
368 }
369 }
370}
371
372#[derive(Debug)]
374#[cfg_attr(feature = "defmt", derive(defmt::Format))]
375pub struct Ipv6OptionsIterator<'a> {
376 pos: usize,
377 length: usize,
378 data: &'a [u8],
379 hit_error: bool,
380}
381
382impl<'a> Ipv6OptionsIterator<'a> {
383 pub fn new(data: &'a [u8]) -> Ipv6OptionsIterator<'a> {
387 let length = data.len();
388 Ipv6OptionsIterator {
389 pos: 0,
390 hit_error: false,
391 length,
392 data,
393 }
394 }
395}
396
397impl<'a> Iterator for Ipv6OptionsIterator<'a> {
398 type Item = Result<Repr<'a>>;
399
400 fn next(&mut self) -> Option<Self::Item> {
401 if self.pos < self.length && !self.hit_error {
402 match Ipv6Option::new_checked(&self.data[self.pos..]) {
405 Ok(hdr) => match Repr::parse(&hdr) {
406 Ok(repr) => {
407 self.pos += repr.buffer_len();
408 Some(Ok(repr))
409 }
410 Err(e) => {
411 self.hit_error = true;
412 Some(Err(e))
413 }
414 },
415 Err(e) => {
416 self.hit_error = true;
417 Some(Err(e))
418 }
419 }
420 } else {
421 None
424 }
425 }
426}
427
428impl<'a> fmt::Display for Repr<'a> {
429 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
430 write!(f, "IPv6 Option ")?;
431 match *self {
432 Repr::Pad1 => write!(f, "{} ", Type::Pad1),
433 Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
434 Repr::RouterAlert(alert) => write!(f, "{} value={:?}", Type::RouterAlert, alert),
435 #[cfg(feature = "proto-rpl")]
436 Repr::Rpl(rpl) => write!(f, "{} {rpl}", Type::Rpl),
437 Repr::Unknown { type_, length, .. } => write!(f, "{type_} length={length} "),
438 }
439 }
440}
441
442#[cfg(test)]
443mod test {
444 use super::*;
445
446 static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
447 static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
448 static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0];
449 static IPV6OPTION_BYTES_ROUTER_ALERT_MLD: [u8; 4] = [0x05, 0x02, 0x00, 0x00];
450 static IPV6OPTION_BYTES_ROUTER_ALERT_RSVP: [u8; 4] = [0x05, 0x02, 0x00, 0x01];
451 static IPV6OPTION_BYTES_ROUTER_ALERT_ACTIVE_NETWORKS: [u8; 4] = [0x05, 0x02, 0x00, 0x02];
452 static IPV6OPTION_BYTES_ROUTER_ALERT_UNKNOWN: [u8; 4] = [0x05, 0x02, 0xbe, 0xef];
453 #[cfg(feature = "proto-rpl")]
454 static IPV6OPTION_BYTES_RPL: [u8; 6] = [0x63, 0x04, 0x00, 0x1e, 0x08, 0x00];
455
456 #[test]
457 fn test_check_len() {
458 let bytes = [0u8];
459 assert_eq!(
461 Err(Error),
462 Ipv6Option::new_unchecked(&bytes[..0]).check_len()
463 );
464 assert_eq!(
466 Ok(()),
467 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()
468 );
469
470 assert_eq!(
472 Err(Error),
473 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()
474 );
475 assert_eq!(
477 Ok(()),
478 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
479 );
480
481 assert_eq!(
483 Err(Error),
484 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD[..3]).check_len()
485 );
486 assert_eq!(
488 Ok(()),
489 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD).check_len()
490 );
491
492 assert_eq!(
494 Err(Error),
495 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()
496 );
497 assert_eq!(
498 Err(Error),
499 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()
500 );
501 assert_eq!(
503 Ok(()),
504 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()
505 );
506
507 #[cfg(feature = "proto-rpl")]
508 {
509 assert_eq!(
510 Ok(()),
511 Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL).check_len()
512 );
513 }
514 }
515
516 #[test]
517 #[should_panic(expected = "index out of bounds")]
518 fn test_data_len() {
519 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
520 opt.data_len();
521 }
522
523 #[test]
524 fn test_option_deconstruct() {
525 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
527 assert_eq!(opt.option_type(), Type::Pad1);
528
529 let bytes: [u8; 2] = [0x1, 0x0];
531 let opt = Ipv6Option::new_unchecked(&bytes);
532 assert_eq!(opt.option_type(), Type::PadN);
533 assert_eq!(opt.data_len(), 0);
534
535 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN);
537 assert_eq!(opt.option_type(), Type::PadN);
538 assert_eq!(opt.data_len(), 1);
539 assert_eq!(opt.data(), &[0]);
540
541 let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
543 let opt = Ipv6Option::new_unchecked(&bytes);
544 assert_eq!(opt.option_type(), Type::PadN);
545 assert_eq!(opt.data_len(), 7);
546 assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]);
547
548 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
550 assert_eq!(opt.option_type(), Type::RouterAlert);
551 assert_eq!(opt.data_len(), 2);
552 assert_eq!(opt.data(), &[0, 0]);
553
554 let bytes: [u8; 1] = [0xff];
556 let opt = Ipv6Option::new_unchecked(&bytes);
557 assert_eq!(opt.option_type(), Type::Unknown(255));
558
559 assert_eq!(Ipv6Option::new_checked(&bytes), Err(Error));
561
562 #[cfg(feature = "proto-rpl")]
563 {
564 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
565 assert_eq!(opt.option_type(), Type::Rpl);
566 assert_eq!(opt.data_len(), 4);
567 assert_eq!(opt.data(), &[0x00, 0x1e, 0x08, 0x00]);
568 }
569 }
570
571 #[test]
572 fn test_option_parse() {
573 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
575 let pad1 = Repr::parse(&opt).unwrap();
576 assert_eq!(pad1, Repr::Pad1);
577 assert_eq!(pad1.buffer_len(), 1);
578
579 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN);
581 let padn = Repr::parse(&opt).unwrap();
582 assert_eq!(padn, Repr::PadN(1));
583 assert_eq!(padn.buffer_len(), 3);
584
585 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
587 let alert = Repr::parse(&opt).unwrap();
588 assert_eq!(
589 alert,
590 Repr::RouterAlert(RouterAlert::MulticastListenerDiscovery)
591 );
592 assert_eq!(alert.buffer_len(), 4);
593
594 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_RSVP);
596 let alert = Repr::parse(&opt).unwrap();
597 assert_eq!(alert, Repr::RouterAlert(RouterAlert::Rsvp));
598 assert_eq!(alert.buffer_len(), 4);
599
600 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_ACTIVE_NETWORKS);
602 let alert = Repr::parse(&opt).unwrap();
603 assert_eq!(alert, Repr::RouterAlert(RouterAlert::ActiveNetworks));
604 assert_eq!(alert.buffer_len(), 4);
605
606 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_UNKNOWN);
608 let alert = Repr::parse(&opt).unwrap();
609 assert_eq!(alert, Repr::RouterAlert(RouterAlert::Unknown(0xbeef)));
610 assert_eq!(alert.buffer_len(), 4);
611
612 let opt = Ipv6Option::new_unchecked(&[0x05, 0x03, 0x00, 0x00, 0x00]);
614 let alert = Repr::parse(&opt);
615 assert_eq!(alert, Err(Error));
616
617 let data = [0u8; 3];
619 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
620 let unknown = Repr::parse(&opt).unwrap();
621 assert_eq!(
622 unknown,
623 Repr::Unknown {
624 type_: Type::Unknown(255),
625 length: 3,
626 data: &data
627 }
628 );
629
630 #[cfg(feature = "proto-rpl")]
631 {
632 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
633 let rpl = Repr::parse(&opt).unwrap();
634
635 assert_eq!(
636 rpl,
637 Repr::Rpl(crate::wire::RplHopByHopRepr {
638 down: false,
639 rank_error: false,
640 forwarding_error: false,
641 instance_id: crate::wire::RplInstanceId::from(0x1e),
642 sender_rank: 0x0800,
643 })
644 );
645 }
646 }
647
648 #[test]
649 fn test_option_emit() {
650 let repr = Repr::Pad1;
651 let mut bytes = [255u8; 1]; let mut opt = Ipv6Option::new_unchecked(&mut bytes);
653 repr.emit(&mut opt);
654 assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PAD1);
655
656 let repr = Repr::PadN(1);
657 let mut bytes = [255u8; 3]; let mut opt = Ipv6Option::new_unchecked(&mut bytes);
659 repr.emit(&mut opt);
660 assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
661
662 let repr = Repr::RouterAlert(RouterAlert::MulticastListenerDiscovery);
663 let mut bytes = [255u8; 4]; let mut opt = Ipv6Option::new_unchecked(&mut bytes);
665 repr.emit(&mut opt);
666 assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
667
668 let data = [0u8; 3];
669 let repr = Repr::Unknown {
670 type_: Type::Unknown(255),
671 length: 3,
672 data: &data,
673 };
674 let mut bytes = [254u8; 5]; let mut opt = Ipv6Option::new_unchecked(&mut bytes);
676 repr.emit(&mut opt);
677 assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_UNKNOWN);
678
679 #[cfg(feature = "proto-rpl")]
680 {
681 let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
682 let rpl = Repr::parse(&opt).unwrap();
683 let mut bytes = [0u8; 6];
684 rpl.emit(&mut Ipv6Option::new_unchecked(&mut bytes));
685
686 assert_eq!(&bytes, &IPV6OPTION_BYTES_RPL);
687 }
688 }
689
690 #[test]
691 fn test_failure_type() {
692 let mut failure_type: FailureType = Type::Pad1.into();
693 assert_eq!(failure_type, FailureType::Skip);
694 failure_type = Type::PadN.into();
695 assert_eq!(failure_type, FailureType::Skip);
696 failure_type = Type::RouterAlert.into();
697 assert_eq!(failure_type, FailureType::Skip);
698 failure_type = Type::Unknown(0b01000001).into();
699 assert_eq!(failure_type, FailureType::Discard);
700 failure_type = Type::Unknown(0b10100000).into();
701 assert_eq!(failure_type, FailureType::DiscardSendAll);
702 failure_type = Type::Unknown(0b11000100).into();
703 assert_eq!(failure_type, FailureType::DiscardSendUnicast);
704 }
705
706 #[test]
707 fn test_options_iter() {
708 let options = [
709 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x05,
710 0x02, 0x00, 0x01, 0x01, 0x08, 0x00,
711 ];
712
713 let iterator = Ipv6OptionsIterator::new(&options);
714 for (i, opt) in iterator.enumerate() {
715 match (i, opt) {
716 (0, Ok(Repr::Pad1)) => continue,
717 (1, Ok(Repr::PadN(1))) => continue,
718 (2, Ok(Repr::PadN(2))) => continue,
719 (3, Ok(Repr::PadN(0))) => continue,
720 (4, Ok(Repr::Pad1)) => continue,
721 (
722 5,
723 Ok(Repr::Unknown {
724 type_: Type::Unknown(0x11),
725 length: 0,
726 ..
727 }),
728 ) => continue,
729 (6, Ok(Repr::RouterAlert(RouterAlert::Rsvp))) => continue,
730 (7, Err(Error)) => continue,
731 (i, res) => panic!("Unexpected option `{res:?}` at index {i}"),
732 }
733 }
734 }
735}