123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768 |
- use bitflags::bitflags;
- use byteorder::{ByteOrder, NetworkEndian};
- use core::fmt;
- use super::{Error, Result};
- use crate::time::Duration;
- use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN};
- use crate::wire::RawHardwareAddress;
- enum_with_unknown! {
- /// NDISC Option Type
- pub enum Type(u8) {
- /// Source Link-layer Address
- SourceLinkLayerAddr = 0x1,
- /// Target Link-layer Address
- TargetLinkLayerAddr = 0x2,
- /// Prefix Information
- PrefixInformation = 0x3,
- /// Redirected Header
- RedirectedHeader = 0x4,
- /// MTU
- Mtu = 0x5
- }
- }
- impl fmt::Display for Type {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Type::SourceLinkLayerAddr => write!(f, "source link-layer address"),
- Type::TargetLinkLayerAddr => write!(f, "target link-layer address"),
- Type::PrefixInformation => write!(f, "prefix information"),
- Type::RedirectedHeader => write!(f, "redirected header"),
- Type::Mtu => write!(f, "mtu"),
- Type::Unknown(id) => write!(f, "{id}"),
- }
- }
- }
- bitflags! {
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct PrefixInfoFlags: u8 {
- const ON_LINK = 0b10000000;
- const ADDRCONF = 0b01000000;
- }
- }
- /// A read/write wrapper around an [NDISC Option].
- ///
- /// [NDISC Option]: https://tools.ietf.org/html/rfc4861#section-4.6
- #[derive(Debug, PartialEq, Eq)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct NdiscOption<T: AsRef<[u8]>> {
- buffer: T,
- }
- // Format of an NDISC Option
- //
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type | Length | ... |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // ~ ... ~
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- //
- // See https://tools.ietf.org/html/rfc4861#section-4.6 for details.
- mod field {
- #![allow(non_snake_case)]
- use crate::wire::field::*;
- // 8-bit identifier of the type of option.
- pub const TYPE: usize = 0;
- // 8-bit unsigned integer. Length of the option, in units of 8 octets.
- pub const LENGTH: usize = 1;
- // Minimum length of an option.
- pub const MIN_OPT_LEN: usize = 8;
- // Variable-length field. Option-Type-specific data.
- pub const fn DATA(length: u8) -> Field {
- 2..length as usize * 8
- }
- // Source/Target Link-layer Option fields.
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type | Length | Link-Layer Address ...
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // Prefix Information Option fields.
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type | Length | Prefix Length |L|A| Reserved1 |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Valid Lifetime |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Preferred Lifetime |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Reserved2 |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // + +
- // | |
- // + Prefix +
- // | |
- // + +
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // Prefix length.
- pub const PREFIX_LEN: usize = 2;
- // Flags field of prefix header.
- pub const FLAGS: usize = 3;
- // Valid lifetime.
- pub const VALID_LT: Field = 4..8;
- // Preferred lifetime.
- pub const PREF_LT: Field = 8..12;
- // Reserved bits
- pub const PREF_RESERVED: Field = 12..16;
- // Prefix
- pub const PREFIX: Field = 16..32;
- // Redirected Header Option fields.
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type | Length | Reserved |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Reserved |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // ~ IP header + data ~
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // Reserved bits.
- pub const REDIRECTED_RESERVED: Field = 2..8;
- pub const REDIR_MIN_SZ: usize = 48;
- // MTU Option fields
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type | Length | Reserved |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | MTU |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // MTU
- pub const MTU: Field = 4..8;
- }
- /// Core getter methods relevant to any type of NDISC option.
- impl<T: AsRef<[u8]>> NdiscOption<T> {
- /// Create a raw octet buffer with an NDISC Option structure.
- pub const fn new_unchecked(buffer: T) -> NdiscOption<T> {
- NdiscOption { buffer }
- }
- /// Shorthand for a combination of [new_unchecked] and [check_len].
- ///
- /// [new_unchecked]: #method.new_unchecked
- /// [check_len]: #method.check_len
- pub fn new_checked(buffer: T) -> Result<NdiscOption<T>> {
- let opt = Self::new_unchecked(buffer);
- opt.check_len()?;
- // A data length field of 0 is invalid.
- if opt.data_len() == 0 {
- return Err(Error);
- }
- Ok(opt)
- }
- /// Ensure that no accessor method will panic if called.
- /// Returns `Err(Error)` if the buffer is too short.
- ///
- /// The result of this check is invalidated by calling [set_data_len].
- ///
- /// [set_data_len]: #method.set_data_len
- pub fn check_len(&self) -> Result<()> {
- let data = self.buffer.as_ref();
- let len = data.len();
- if len < field::MIN_OPT_LEN {
- Err(Error)
- } else {
- let data_range = field::DATA(data[field::LENGTH]);
- if len < data_range.end {
- Err(Error)
- } else {
- match self.option_type() {
- Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()),
- Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()),
- Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()),
- Type::Unknown(_) => Ok(()),
- _ => Err(Error),
- }
- }
- }
- }
- /// Consume the NDISC option, returning the underlying buffer.
- pub fn into_inner(self) -> T {
- self.buffer
- }
- /// Return the option type.
- #[inline]
- pub fn option_type(&self) -> Type {
- let data = self.buffer.as_ref();
- Type::from(data[field::TYPE])
- }
- /// Return the length of the data.
- #[inline]
- pub fn data_len(&self) -> u8 {
- let data = self.buffer.as_ref();
- data[field::LENGTH]
- }
- }
- /// Getter methods only relevant for Source/Target Link-layer Address options.
- impl<T: AsRef<[u8]>> NdiscOption<T> {
- /// Return the Source/Target Link-layer Address.
- #[inline]
- pub fn link_layer_addr(&self) -> RawHardwareAddress {
- let len = MAX_HARDWARE_ADDRESS_LEN.min(self.data_len() as usize * 8 - 2);
- let data = self.buffer.as_ref();
- RawHardwareAddress::from_bytes(&data[2..len + 2])
- }
- }
- /// Getter methods only relevant for the MTU option.
- impl<T: AsRef<[u8]>> NdiscOption<T> {
- /// Return the MTU value.
- #[inline]
- pub fn mtu(&self) -> u32 {
- let data = self.buffer.as_ref();
- NetworkEndian::read_u32(&data[field::MTU])
- }
- }
- /// Getter methods only relevant for the Prefix Information option.
- impl<T: AsRef<[u8]>> NdiscOption<T> {
- /// Return the prefix length.
- #[inline]
- pub fn prefix_len(&self) -> u8 {
- self.buffer.as_ref()[field::PREFIX_LEN]
- }
- /// Return the prefix information flags.
- #[inline]
- pub fn prefix_flags(&self) -> PrefixInfoFlags {
- PrefixInfoFlags::from_bits_truncate(self.buffer.as_ref()[field::FLAGS])
- }
- /// Return the valid lifetime of the prefix.
- #[inline]
- pub fn valid_lifetime(&self) -> Duration {
- let data = self.buffer.as_ref();
- Duration::from_secs(NetworkEndian::read_u32(&data[field::VALID_LT]) as u64)
- }
- /// Return the preferred lifetime of the prefix.
- #[inline]
- pub fn preferred_lifetime(&self) -> Duration {
- let data = self.buffer.as_ref();
- Duration::from_secs(NetworkEndian::read_u32(&data[field::PREF_LT]) as u64)
- }
- /// Return the prefix.
- #[inline]
- pub fn prefix(&self) -> Ipv6Address {
- let data = self.buffer.as_ref();
- Ipv6Address::from_bytes(&data[field::PREFIX])
- }
- }
- impl<'a, T: AsRef<[u8]> + ?Sized> NdiscOption<&'a T> {
- /// Return the option data.
- #[inline]
- pub fn data(&self) -> &'a [u8] {
- let len = self.data_len();
- let data = self.buffer.as_ref();
- &data[field::DATA(len)]
- }
- }
- /// Core setter methods relevant to any type of NDISC option.
- impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
- /// Set the option type.
- #[inline]
- pub fn set_option_type(&mut self, value: Type) {
- let data = self.buffer.as_mut();
- data[field::TYPE] = value.into();
- }
- /// Set the option data length.
- #[inline]
- pub fn set_data_len(&mut self, value: u8) {
- let data = self.buffer.as_mut();
- data[field::LENGTH] = value;
- }
- }
- /// Setter methods only relevant for Source/Target Link-layer Address options.
- impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
- /// Set the Source/Target Link-layer Address.
- #[inline]
- pub fn set_link_layer_addr(&mut self, addr: RawHardwareAddress) {
- let data = self.buffer.as_mut();
- data[2..2 + addr.len()].copy_from_slice(addr.as_bytes())
- }
- }
- /// Setter methods only relevant for the MTU option.
- impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
- /// Set the MTU value.
- #[inline]
- pub fn set_mtu(&mut self, value: u32) {
- let data = self.buffer.as_mut();
- NetworkEndian::write_u32(&mut data[field::MTU], value);
- }
- }
- /// Setter methods only relevant for the Prefix Information option.
- impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
- /// Set the prefix length.
- #[inline]
- pub fn set_prefix_len(&mut self, value: u8) {
- self.buffer.as_mut()[field::PREFIX_LEN] = value;
- }
- /// Set the prefix information flags.
- #[inline]
- pub fn set_prefix_flags(&mut self, flags: PrefixInfoFlags) {
- self.buffer.as_mut()[field::FLAGS] = flags.bits();
- }
- /// Set the valid lifetime of the prefix.
- #[inline]
- pub fn set_valid_lifetime(&mut self, time: Duration) {
- let data = self.buffer.as_mut();
- NetworkEndian::write_u32(&mut data[field::VALID_LT], time.secs() as u32);
- }
- /// Set the preferred lifetime of the prefix.
- #[inline]
- pub fn set_preferred_lifetime(&mut self, time: Duration) {
- let data = self.buffer.as_mut();
- NetworkEndian::write_u32(&mut data[field::PREF_LT], time.secs() as u32);
- }
- /// Clear the reserved bits.
- #[inline]
- pub fn clear_prefix_reserved(&mut self) {
- let data = self.buffer.as_mut();
- NetworkEndian::write_u32(&mut data[field::PREF_RESERVED], 0);
- }
- /// Set the prefix.
- #[inline]
- pub fn set_prefix(&mut self, addr: Ipv6Address) {
- let data = self.buffer.as_mut();
- data[field::PREFIX].copy_from_slice(addr.as_bytes());
- }
- }
- /// Setter methods only relevant for the Redirected Header option.
- impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
- /// Clear the reserved bits.
- #[inline]
- pub fn clear_redirected_reserved(&mut self) {
- let data = self.buffer.as_mut();
- data[field::REDIRECTED_RESERVED].fill_with(|| 0);
- }
- }
- impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
- /// Return a mutable pointer to the option data.
- #[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 NdiscOption<&'a T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match Repr::parse(self) {
- Ok(repr) => write!(f, "{repr}"),
- Err(err) => {
- write!(f, "NDISC Option ({err})")?;
- Ok(())
- }
- }
- }
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct PrefixInformation {
- pub prefix_len: u8,
- pub flags: PrefixInfoFlags,
- pub valid_lifetime: Duration,
- pub preferred_lifetime: Duration,
- pub prefix: Ipv6Address,
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub struct RedirectedHeader<'a> {
- pub header: Ipv6Repr,
- pub data: &'a [u8],
- }
- /// A high-level representation of an NDISC Option.
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- pub enum Repr<'a> {
- SourceLinkLayerAddr(RawHardwareAddress),
- TargetLinkLayerAddr(RawHardwareAddress),
- PrefixInformation(PrefixInformation),
- RedirectedHeader(RedirectedHeader<'a>),
- Mtu(u32),
- Unknown {
- type_: u8,
- length: u8,
- data: &'a [u8],
- },
- }
- impl<'a> Repr<'a> {
- /// Parse an NDISC Option and return a high-level representation.
- pub fn parse<T>(opt: &NdiscOption<&'a T>) -> Result<Repr<'a>>
- where
- T: AsRef<[u8]> + ?Sized,
- {
- match opt.option_type() {
- Type::SourceLinkLayerAddr => {
- if opt.data_len() >= 1 {
- Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr()))
- } else {
- Err(Error)
- }
- }
- Type::TargetLinkLayerAddr => {
- if opt.data_len() >= 1 {
- Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
- } else {
- Err(Error)
- }
- }
- Type::PrefixInformation => {
- if opt.data_len() == 4 {
- Ok(Repr::PrefixInformation(PrefixInformation {
- prefix_len: opt.prefix_len(),
- flags: opt.prefix_flags(),
- valid_lifetime: opt.valid_lifetime(),
- preferred_lifetime: opt.preferred_lifetime(),
- prefix: opt.prefix(),
- }))
- } else {
- Err(Error)
- }
- }
- Type::RedirectedHeader => {
- // If the options data length is less than 6, the option
- // does not have enough data to fill out the IP header
- // and common option fields.
- if opt.data_len() < 6 {
- Err(Error)
- } else {
- let redirected_packet = &opt.data()[field::REDIRECTED_RESERVED.len()..];
- let ip_packet = Ipv6Packet::new_checked(redirected_packet)?;
- let ip_repr = Ipv6Repr::parse(&ip_packet)?;
- Ok(Repr::RedirectedHeader(RedirectedHeader {
- header: ip_repr,
- data: &redirected_packet[ip_repr.buffer_len()..][..ip_repr.payload_len],
- }))
- }
- }
- Type::Mtu => {
- if opt.data_len() == 1 {
- Ok(Repr::Mtu(opt.mtu()))
- } else {
- Err(Error)
- }
- }
- Type::Unknown(id) => {
- // A length of 0 is invalid.
- if opt.data_len() != 0 {
- Ok(Repr::Unknown {
- type_: id,
- length: opt.data_len(),
- data: opt.data(),
- })
- } else {
- Err(Error)
- }
- }
- }
- }
- /// Return the length of a header that will be emitted from this high-level representation.
- pub const fn buffer_len(&self) -> usize {
- match self {
- &Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => {
- let len = 2 + addr.len();
- // Round up to next multiple of 8
- (len + 7) / 8 * 8
- }
- &Repr::PrefixInformation(_) => field::PREFIX.end,
- &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
- (8 + header.buffer_len() + data.len() + 7) / 8 * 8
- }
- &Repr::Mtu(_) => field::MTU.end,
- &Repr::Unknown { length, .. } => field::DATA(length).end,
- }
- }
- /// Emit a high-level representation into an NDISC Option.
- pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>)
- where
- T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
- {
- match *self {
- Repr::SourceLinkLayerAddr(addr) => {
- opt.set_option_type(Type::SourceLinkLayerAddr);
- let opt_len = addr.len() + 2;
- opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
- opt.set_link_layer_addr(addr);
- }
- Repr::TargetLinkLayerAddr(addr) => {
- opt.set_option_type(Type::TargetLinkLayerAddr);
- let opt_len = addr.len() + 2;
- opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
- opt.set_link_layer_addr(addr);
- }
- Repr::PrefixInformation(PrefixInformation {
- prefix_len,
- flags,
- valid_lifetime,
- preferred_lifetime,
- prefix,
- }) => {
- opt.clear_prefix_reserved();
- opt.set_option_type(Type::PrefixInformation);
- opt.set_data_len(4);
- opt.set_prefix_len(prefix_len);
- opt.set_prefix_flags(flags);
- opt.set_valid_lifetime(valid_lifetime);
- opt.set_preferred_lifetime(preferred_lifetime);
- opt.set_prefix(prefix);
- }
- Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
- // TODO(thvdveld): I think we need to check if the data we are sending is not
- // exceeding the MTU.
- opt.clear_redirected_reserved();
- opt.set_option_type(Type::RedirectedHeader);
- opt.set_data_len((((8 + header.buffer_len() + data.len()) + 7) / 8) as u8);
- let mut packet = &mut opt.data_mut()[field::REDIRECTED_RESERVED.end - 2..];
- let mut ip_packet = Ipv6Packet::new_unchecked(&mut packet);
- header.emit(&mut ip_packet);
- ip_packet.payload_mut().copy_from_slice(data);
- }
- Repr::Mtu(mtu) => {
- opt.set_option_type(Type::Mtu);
- opt.set_data_len(1);
- opt.set_mtu(mtu);
- }
- Repr::Unknown {
- type_: id,
- length,
- data,
- } => {
- opt.set_option_type(Type::Unknown(id));
- opt.set_data_len(length);
- opt.data_mut().copy_from_slice(data);
- }
- }
- }
- }
- impl<'a> fmt::Display for Repr<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "NDISC Option: ")?;
- match *self {
- Repr::SourceLinkLayerAddr(addr) => {
- write!(f, "SourceLinkLayer addr={addr}")
- }
- Repr::TargetLinkLayerAddr(addr) => {
- write!(f, "TargetLinkLayer addr={addr}")
- }
- Repr::PrefixInformation(PrefixInformation {
- prefix, prefix_len, ..
- }) => {
- write!(f, "PrefixInformation prefix={prefix}/{prefix_len}")
- }
- Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
- write!(f, "RedirectedHeader header={header}")
- }
- Repr::Mtu(mtu) => {
- write!(f, "MTU mtu={mtu}")
- }
- Repr::Unknown {
- type_: id, length, ..
- } => {
- write!(f, "Unknown({id}) length={length}")
- }
- }
- }
- }
- use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
- impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
- fn pretty_print(
- buffer: &dyn AsRef<[u8]>,
- f: &mut fmt::Formatter,
- indent: &mut PrettyIndent,
- ) -> fmt::Result {
- match NdiscOption::new_checked(buffer) {
- Err(err) => write!(f, "{indent}({err})"),
- Ok(ndisc) => match Repr::parse(&ndisc) {
- Err(_) => Ok(()),
- Ok(repr) => {
- write!(f, "{indent}{repr}")
- }
- },
- }
- }
- }
- #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
- #[cfg(test)]
- mod test {
- use super::Error;
- use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type};
- use crate::time::Duration;
- use crate::wire::Ipv6Address;
- #[cfg(feature = "medium-ethernet")]
- use crate::wire::EthernetAddress;
- #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
- use crate::wire::Ieee802154Address;
- static PREFIX_OPT_BYTES: [u8; 32] = [
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
- 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01,
- ];
- #[test]
- fn test_deconstruct() {
- let opt = NdiscOption::new_unchecked(&PREFIX_OPT_BYTES[..]);
- assert_eq!(opt.option_type(), Type::PrefixInformation);
- assert_eq!(opt.data_len(), 4);
- assert_eq!(opt.prefix_len(), 64);
- assert_eq!(
- opt.prefix_flags(),
- PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF
- );
- assert_eq!(opt.valid_lifetime(), Duration::from_secs(900));
- assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000));
- assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
- }
- #[test]
- fn test_construct() {
- let mut bytes = [0x00; 32];
- let mut opt = NdiscOption::new_unchecked(&mut bytes[..]);
- opt.set_option_type(Type::PrefixInformation);
- opt.set_data_len(4);
- opt.set_prefix_len(64);
- opt.set_prefix_flags(PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF);
- opt.set_valid_lifetime(Duration::from_secs(900));
- opt.set_preferred_lifetime(Duration::from_secs(1000));
- opt.set_prefix(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
- assert_eq!(&PREFIX_OPT_BYTES[..], &*opt.into_inner());
- }
- #[test]
- fn test_short_packet() {
- assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error));
- let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
- assert_eq!(NdiscOption::new_checked(&bytes), Err(Error));
- }
- #[cfg(feature = "medium-ethernet")]
- #[test]
- fn test_repr_parse_link_layer_opt_ethernet() {
- let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34];
- let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
- {
- assert_eq!(
- Repr::parse(&NdiscOption::new_unchecked(&bytes)),
- Ok(Repr::SourceLinkLayerAddr(addr.into()))
- );
- }
- bytes[0] = 0x02;
- {
- assert_eq!(
- Repr::parse(&NdiscOption::new_unchecked(&bytes)),
- Ok(Repr::TargetLinkLayerAddr(addr.into()))
- );
- }
- }
- #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
- #[test]
- fn test_repr_parse_link_layer_opt_ieee802154() {
- let mut bytes = [
- 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00,
- ];
- let addr = Ieee802154Address::Extended([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
- {
- assert_eq!(
- Repr::parse(&NdiscOption::new_unchecked(&bytes)),
- Ok(Repr::SourceLinkLayerAddr(addr.into()))
- );
- }
- bytes[0] = 0x02;
- {
- assert_eq!(
- Repr::parse(&NdiscOption::new_unchecked(&bytes)),
- Ok(Repr::TargetLinkLayerAddr(addr.into()))
- );
- }
- }
- #[test]
- fn test_repr_parse_prefix_info() {
- let repr = Repr::PrefixInformation(PrefixInformation {
- prefix_len: 64,
- flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
- valid_lifetime: Duration::from_secs(900),
- preferred_lifetime: Duration::from_secs(1000),
- prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
- });
- assert_eq!(
- Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)),
- Ok(repr)
- );
- }
- #[test]
- fn test_repr_emit_prefix_info() {
- let mut bytes = [0x2a; 32];
- let repr = Repr::PrefixInformation(PrefixInformation {
- prefix_len: 64,
- flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
- valid_lifetime: Duration::from_secs(900),
- preferred_lifetime: Duration::from_secs(1000),
- prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
- });
- let mut opt = NdiscOption::new_unchecked(&mut bytes);
- repr.emit(&mut opt);
- assert_eq!(&opt.into_inner()[..], &PREFIX_OPT_BYTES[..]);
- }
- #[test]
- fn test_repr_parse_mtu() {
- let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc];
- assert_eq!(
- Repr::parse(&NdiscOption::new_unchecked(&bytes)),
- Ok(Repr::Mtu(1500))
- );
- }
- }
|