123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- use super::{Error, Result};
- #[cfg(feature = "proto-rpl")]
- use super::{RplHopByHopPacket, RplHopByHopRepr};
- use core::fmt;
- enum_with_unknown! {
-
- pub enum Type(u8) {
-
- Pad1 = 0,
-
- PadN = 1,
-
- Rpl = 0x63,
- }
- }
- impl fmt::Display for Type {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Type::Pad1 => write!(f, "Pad1"),
- Type::PadN => write!(f, "PadN"),
- Type::Rpl => write!(f, "RPL"),
- Type::Unknown(id) => write!(f, "{id}"),
- }
- }
- }
- enum_with_unknown! {
-
-
- pub enum FailureType(u8) {
-
- Skip = 0b00000000,
-
- Discard = 0b01000000,
-
- DiscardSendAll = 0b10000000,
-
-
- DiscardSendUnicast = 0b11000000,
- }
- }
- impl fmt::Display for FailureType {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- FailureType::Skip => write!(f, "skip"),
- FailureType::Discard => write!(f, "discard"),
- FailureType::DiscardSendAll => write!(f, "discard and send error"),
- FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"),
- FailureType::Unknown(id) => write!(f, "Unknown({id})"),
- }
- }
- }
- impl From<Type> for FailureType {
- fn from(other: Type) -> FailureType {
- let raw: u8 = other.into();
- Self::from(raw & 0b11000000u8)
- }
- }
- #[derive(Debug, PartialEq, Eq)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Ipv6Option<T: AsRef<[u8]>> {
- buffer: T,
- }
- mod field {
- #![allow(non_snake_case)]
- use crate::wire::field::*;
-
- pub const TYPE: usize = 0;
-
- pub const LENGTH: usize = 1;
-
- pub const fn DATA(length: u8) -> Field {
- 2..length as usize + 2
- }
- }
- impl<T: AsRef<[u8]>> Ipv6Option<T> {
-
- pub const fn new_unchecked(buffer: T) -> Ipv6Option<T> {
- Ipv6Option { buffer }
- }
-
-
-
-
- pub fn new_checked(buffer: T) -> Result<Ipv6Option<T>> {
- let opt = Self::new_unchecked(buffer);
- opt.check_len()?;
- Ok(opt)
- }
-
-
-
-
-
-
- pub fn check_len(&self) -> Result<()> {
- let data = self.buffer.as_ref();
- let len = data.len();
- if len < field::LENGTH {
- return Err(Error);
- }
- if self.option_type() == Type::Pad1 {
- return Ok(());
- }
- if len == field::LENGTH {
- return Err(Error);
- }
- let df = field::DATA(data[field::LENGTH]);
- if len < df.end {
- return Err(Error);
- }
- Ok(())
- }
-
- pub fn into_inner(self) -> T {
- self.buffer
- }
-
- #[inline]
- pub fn option_type(&self) -> Type {
- let data = self.buffer.as_ref();
- Type::from(data[field::TYPE])
- }
-
-
-
-
- #[inline]
- pub fn data_len(&self) -> u8 {
- let data = self.buffer.as_ref();
- data[field::LENGTH]
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> Ipv6Option<&'a T> {
-
-
-
-
- #[inline]
- pub fn data(&self) -> &'a [u8] {
- let len = self.data_len();
- let data = self.buffer.as_ref();
- &data[field::DATA(len)]
- }
- }
- impl<T: AsRef<[u8]> + AsMut<[u8]>> Ipv6Option<T> {
-
- #[inline]
- pub fn set_option_type(&mut self, value: Type) {
- let data = self.buffer.as_mut();
- data[field::TYPE] = value.into();
- }
-
-
-
-
- #[inline]
- pub fn set_data_len(&mut self, value: u8) {
- let data = self.buffer.as_mut();
- data[field::LENGTH] = value;
- }
- }
- impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Ipv6Option<&'a mut T> {
-
-
-
-
- #[inline]
- pub fn data_mut(&mut self) -> &mut [u8] {
- let len = self.data_len();
- let data = self.buffer.as_mut();
- &mut data[field::DATA(len)]
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match Repr::parse(self) {
- Ok(repr) => write!(f, "{repr}"),
- Err(err) => {
- write!(f, "IPv6 Extension Option ({err})")?;
- Ok(())
- }
- }
- }
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- #[non_exhaustive]
- pub enum Repr<'a> {
- Pad1,
- PadN(u8),
- #[cfg(feature = "proto-rpl")]
- Rpl(RplHopByHopRepr),
- Unknown {
- type_: Type,
- length: u8,
- data: &'a [u8],
- },
- }
- impl<'a> Repr<'a> {
-
- pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>>
- where
- T: AsRef<[u8]> + ?Sized,
- {
- match opt.option_type() {
- Type::Pad1 => Ok(Repr::Pad1),
- Type::PadN => Ok(Repr::PadN(opt.data_len())),
- #[cfg(feature = "proto-rpl")]
- Type::Rpl => Ok(Repr::Rpl(RplHopByHopRepr::parse(
- &RplHopByHopPacket::new_checked(opt.data())?,
- ))),
- #[cfg(not(feature = "proto-rpl"))]
- Type::Rpl => Ok(Repr::Unknown {
- type_: Type::Rpl,
- length: opt.data_len(),
- data: opt.data(),
- }),
- unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown {
- type_: unknown_type,
- length: opt.data_len(),
- data: opt.data(),
- }),
- }
- }
-
- pub const fn buffer_len(&self) -> usize {
- match *self {
- Repr::Pad1 => 1,
- Repr::PadN(length) => field::DATA(length).end,
- #[cfg(feature = "proto-rpl")]
- Repr::Rpl(opt) => field::DATA(opt.buffer_len() as u8).end,
- Repr::Unknown { length, .. } => field::DATA(length).end,
- }
- }
-
- pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) {
- match *self {
- Repr::Pad1 => opt.set_option_type(Type::Pad1),
- Repr::PadN(len) => {
- opt.set_option_type(Type::PadN);
- opt.set_data_len(len);
-
- for x in opt.data_mut().iter_mut() {
- *x = 0
- }
- }
- #[cfg(feature = "proto-rpl")]
- Repr::Rpl(rpl) => {
- opt.set_option_type(Type::Rpl);
- opt.set_data_len(4);
- rpl.emit(&mut crate::wire::RplHopByHopPacket::new_unchecked(
- opt.data_mut(),
- ));
- }
- Repr::Unknown {
- type_,
- length,
- data,
- } => {
- opt.set_option_type(type_);
- opt.set_data_len(length);
- opt.data_mut().copy_from_slice(&data[..length as usize]);
- }
- }
- }
- }
- #[derive(Debug)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct Ipv6OptionsIterator<'a> {
- pos: usize,
- length: usize,
- data: &'a [u8],
- hit_error: bool,
- }
- impl<'a> Ipv6OptionsIterator<'a> {
-
-
-
-
-
-
-
- pub fn new(data: &'a [u8], length: usize) -> Ipv6OptionsIterator<'a> {
- assert!(length <= data.len());
- Ipv6OptionsIterator {
- pos: 0,
- hit_error: false,
- length,
- data,
- }
- }
- }
- impl<'a> Iterator for Ipv6OptionsIterator<'a> {
- type Item = Result<Repr<'a>>;
- fn next(&mut self) -> Option<Self::Item> {
- if self.pos < self.length && !self.hit_error {
-
-
- match Ipv6Option::new_checked(&self.data[self.pos..]) {
- Ok(hdr) => match Repr::parse(&hdr) {
- Ok(repr) => {
- self.pos += repr.buffer_len();
- Some(Ok(repr))
- }
- Err(e) => {
- self.hit_error = true;
- Some(Err(e))
- }
- },
- Err(e) => {
- self.hit_error = true;
- Some(Err(e))
- }
- }
- } else {
-
-
- None
- }
- }
- }
- impl<'a> fmt::Display for Repr<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "IPv6 Option ")?;
- match *self {
- Repr::Pad1 => write!(f, "{} ", Type::Pad1),
- Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
- #[cfg(feature = "proto-rpl")]
- Repr::Rpl(rpl) => write!(f, "{} {rpl}", Type::Rpl),
- Repr::Unknown { type_, length, .. } => write!(f, "{type_} length={length} "),
- }
- }
- }
- #[cfg(test)]
- mod test {
- use super::*;
- static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
- static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
- static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0];
- #[cfg(feature = "proto-rpl")]
- static IPV6OPTION_BYTES_RPL: [u8; 6] = [0x63, 0x04, 0x00, 0x1e, 0x08, 0x00];
- #[test]
- fn test_check_len() {
- let bytes = [0u8];
-
- assert_eq!(
- Err(Error),
- Ipv6Option::new_unchecked(&bytes[..0]).check_len()
- );
-
- assert_eq!(
- Ok(()),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()
- );
-
- assert_eq!(
- Err(Error),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()
- );
-
- assert_eq!(
- Ok(()),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
- );
-
- assert_eq!(
- Err(Error),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()
- );
- assert_eq!(
- Err(Error),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()
- );
-
- assert_eq!(
- Ok(()),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()
- );
- #[cfg(feature = "proto-rpl")]
- {
- assert_eq!(
- Ok(()),
- Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL).check_len()
- );
- }
- }
- #[test]
- #[should_panic(expected = "index out of bounds")]
- fn test_data_len() {
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
- opt.data_len();
- }
- #[test]
- fn test_option_deconstruct() {
-
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
- assert_eq!(opt.option_type(), Type::Pad1);
-
- let bytes: [u8; 2] = [0x1, 0x0];
- let opt = Ipv6Option::new_unchecked(&bytes);
- assert_eq!(opt.option_type(), Type::PadN);
- assert_eq!(opt.data_len(), 0);
-
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN);
- assert_eq!(opt.option_type(), Type::PadN);
- assert_eq!(opt.data_len(), 1);
- assert_eq!(opt.data(), &[0]);
-
- let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
- let opt = Ipv6Option::new_unchecked(&bytes);
- assert_eq!(opt.option_type(), Type::PadN);
- assert_eq!(opt.data_len(), 7);
- assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]);
-
- let bytes: [u8; 1] = [0xff];
- let opt = Ipv6Option::new_unchecked(&bytes);
- assert_eq!(opt.option_type(), Type::Unknown(255));
-
- assert_eq!(Ipv6Option::new_checked(&bytes), Err(Error));
- #[cfg(feature = "proto-rpl")]
- {
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
- assert_eq!(opt.option_type(), Type::Rpl);
- assert_eq!(opt.data_len(), 4);
- assert_eq!(opt.data(), &[0x00, 0x1e, 0x08, 0x00]);
- }
- }
- #[test]
- fn test_option_parse() {
-
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
- let pad1 = Repr::parse(&opt).unwrap();
- assert_eq!(pad1, Repr::Pad1);
- assert_eq!(pad1.buffer_len(), 1);
-
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN);
- let padn = Repr::parse(&opt).unwrap();
- assert_eq!(padn, Repr::PadN(1));
- assert_eq!(padn.buffer_len(), 3);
-
- let data = [0u8; 3];
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
- let unknown = Repr::parse(&opt).unwrap();
- assert_eq!(
- unknown,
- Repr::Unknown {
- type_: Type::Unknown(255),
- length: 3,
- data: &data
- }
- );
- #[cfg(feature = "proto-rpl")]
- {
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
- let rpl = Repr::parse(&opt).unwrap();
- assert_eq!(
- rpl,
- Repr::Rpl(crate::wire::RplHopByHopRepr {
- down: false,
- rank_error: false,
- forwarding_error: false,
- instance_id: crate::wire::RplInstanceId::from(0x1e),
- sender_rank: 0x0800,
- })
- );
- }
- }
- #[test]
- fn test_option_emit() {
- let repr = Repr::Pad1;
- let mut bytes = [255u8; 1];
- let mut opt = Ipv6Option::new_unchecked(&mut bytes);
- repr.emit(&mut opt);
- assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PAD1);
- let repr = Repr::PadN(1);
- let mut bytes = [255u8; 3];
- let mut opt = Ipv6Option::new_unchecked(&mut bytes);
- repr.emit(&mut opt);
- assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
- let data = [0u8; 3];
- let repr = Repr::Unknown {
- type_: Type::Unknown(255),
- length: 3,
- data: &data,
- };
- let mut bytes = [254u8; 5];
- let mut opt = Ipv6Option::new_unchecked(&mut bytes);
- repr.emit(&mut opt);
- assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_UNKNOWN);
- #[cfg(feature = "proto-rpl")]
- {
- let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
- let rpl = Repr::parse(&opt).unwrap();
- let mut bytes = [0u8; 6];
- rpl.emit(&mut Ipv6Option::new_unchecked(&mut bytes));
- assert_eq!(&bytes, &IPV6OPTION_BYTES_RPL);
- }
- }
- #[test]
- fn test_failure_type() {
- let mut failure_type: FailureType = Type::Pad1.into();
- assert_eq!(failure_type, FailureType::Skip);
- failure_type = Type::PadN.into();
- assert_eq!(failure_type, FailureType::Skip);
- failure_type = Type::Unknown(0b01000001).into();
- assert_eq!(failure_type, FailureType::Discard);
- failure_type = Type::Unknown(0b10100000).into();
- assert_eq!(failure_type, FailureType::DiscardSendAll);
- failure_type = Type::Unknown(0b11000100).into();
- assert_eq!(failure_type, FailureType::DiscardSendUnicast);
- }
- #[test]
- fn test_options_iter() {
- let options = [
- 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x01,
- 0x08, 0x00,
- ];
- let mut iterator = Ipv6OptionsIterator::new(&options, 0);
- assert_eq!(iterator.next(), None);
- iterator = Ipv6OptionsIterator::new(&options, 16);
- for (i, opt) in iterator.enumerate() {
- match (i, opt) {
- (0, Ok(Repr::Pad1)) => continue,
- (1, Ok(Repr::PadN(1))) => continue,
- (2, Ok(Repr::PadN(2))) => continue,
- (3, Ok(Repr::PadN(0))) => continue,
- (4, Ok(Repr::Pad1)) => continue,
- (
- 5,
- Ok(Repr::Unknown {
- type_: Type::Unknown(0x11),
- length: 0,
- ..
- }),
- ) => continue,
- (6, Err(Error)) => continue,
- (i, res) => panic!("Unexpected option `{res:?}` at index {i}"),
- }
- }
- }
- #[test]
- #[should_panic(expected = "length <= data.len()")]
- fn test_options_iter_truncated() {
- let options = [0x01, 0x02, 0x00, 0x00];
- let _ = Ipv6OptionsIterator::new(&options, 5);
- }
- }
|