1 use bitflags::bitflags;
2 use byteorder::{ByteOrder, NetworkEndian};
3 use core::fmt;
4 
5 use super::{Error, Result};
6 use crate::time::Duration;
7 use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN};
8 
9 use crate::wire::RawHardwareAddress;
10 
11 enum_with_unknown! {
12     /// NDISC Option Type
13     pub enum Type(u8) {
14         /// Source Link-layer Address
15         SourceLinkLayerAddr = 0x1,
16         /// Target Link-layer Address
17         TargetLinkLayerAddr = 0x2,
18         /// Prefix Information
19         PrefixInformation   = 0x3,
20         /// Redirected Header
21         RedirectedHeader    = 0x4,
22         /// MTU
23         Mtu                 = 0x5
24     }
25 }
26 
27 impl fmt::Display for Type {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result28     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29         match self {
30             Type::SourceLinkLayerAddr => write!(f, "source link-layer address"),
31             Type::TargetLinkLayerAddr => write!(f, "target link-layer address"),
32             Type::PrefixInformation => write!(f, "prefix information"),
33             Type::RedirectedHeader => write!(f, "redirected header"),
34             Type::Mtu => write!(f, "mtu"),
35             Type::Unknown(id) => write!(f, "{id}"),
36         }
37     }
38 }
39 
40 bitflags! {
41     #[cfg_attr(feature = "defmt", derive(defmt::Format))]
42     pub struct PrefixInfoFlags: u8 {
43         const ON_LINK  = 0b10000000;
44         const ADDRCONF = 0b01000000;
45     }
46 }
47 
48 /// A read/write wrapper around an [NDISC Option].
49 ///
50 /// [NDISC Option]: https://tools.ietf.org/html/rfc4861#section-4.6
51 #[derive(Debug, PartialEq, Eq)]
52 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
53 pub struct NdiscOption<T: AsRef<[u8]>> {
54     buffer: T,
55 }
56 
57 // Format of an NDISC Option
58 //
59 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 // |     Type      |    Length     |              ...              |
61 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 // ~                              ...                              ~
63 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 //
65 // See https://tools.ietf.org/html/rfc4861#section-4.6 for details.
66 mod field {
67     #![allow(non_snake_case)]
68 
69     use crate::wire::field::*;
70 
71     // 8-bit identifier of the type of option.
72     pub const TYPE: usize = 0;
73     // 8-bit unsigned integer. Length of the option, in units of 8 octets.
74     pub const LENGTH: usize = 1;
75     // Minimum length of an option.
76     pub const MIN_OPT_LEN: usize = 8;
77     // Variable-length field. Option-Type-specific data.
DATA(length: u8) -> Field78     pub const fn DATA(length: u8) -> Field {
79         2..length as usize * 8
80     }
81 
82     // Source/Target Link-layer Option fields.
83     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84     // |     Type      |    Length     |    Link-Layer Address ...
85     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 
87     // Prefix Information Option fields.
88     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
89     //  |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
90     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91     //  |                         Valid Lifetime                        |
92     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93     //  |                       Preferred Lifetime                      |
94     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95     //  |                           Reserved2                           |
96     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97     //  |                                                               |
98     //  +                                                               +
99     //  |                                                               |
100     //  +                            Prefix                             +
101     //  |                                                               |
102     //  +                                                               +
103     //  |                                                               |
104     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
105 
106     // Prefix length.
107     pub const PREFIX_LEN: usize = 2;
108     // Flags field of prefix header.
109     pub const FLAGS: usize = 3;
110     // Valid lifetime.
111     pub const VALID_LT: Field = 4..8;
112     // Preferred lifetime.
113     pub const PREF_LT: Field = 8..12;
114     // Reserved bits
115     pub const PREF_RESERVED: Field = 12..16;
116     // Prefix
117     pub const PREFIX: Field = 16..32;
118 
119     // Redirected Header Option fields.
120     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
121     //  |     Type      |    Length     |            Reserved           |
122     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
123     //  |                           Reserved                            |
124     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
125     //  |                                                               |
126     //  ~                       IP header + data                        ~
127     //  |                                                               |
128     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
129 
130     // Reserved bits.
131     pub const REDIRECTED_RESERVED: Field = 2..8;
132     pub const REDIR_MIN_SZ: usize = 48;
133 
134     // MTU Option fields
135     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
136     //  |     Type      |    Length     |           Reserved            |
137     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138     //  |                              MTU                              |
139     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140 
141     //  MTU
142     pub const MTU: Field = 4..8;
143 }
144 
145 /// Core getter methods relevant to any type of NDISC option.
146 impl<T: AsRef<[u8]>> NdiscOption<T> {
147     /// Create a raw octet buffer with an NDISC Option structure.
new_unchecked(buffer: T) -> NdiscOption<T>148     pub const fn new_unchecked(buffer: T) -> NdiscOption<T> {
149         NdiscOption { buffer }
150     }
151 
152     /// Shorthand for a combination of [new_unchecked] and [check_len].
153     ///
154     /// [new_unchecked]: #method.new_unchecked
155     /// [check_len]: #method.check_len
new_checked(buffer: T) -> Result<NdiscOption<T>>156     pub fn new_checked(buffer: T) -> Result<NdiscOption<T>> {
157         let opt = Self::new_unchecked(buffer);
158         opt.check_len()?;
159 
160         // A data length field of 0 is invalid.
161         if opt.data_len() == 0 {
162             return Err(Error);
163         }
164 
165         Ok(opt)
166     }
167 
168     /// Ensure that no accessor method will panic if called.
169     /// Returns `Err(Error)` if the buffer is too short.
170     ///
171     /// The result of this check is invalidated by calling [set_data_len].
172     ///
173     /// [set_data_len]: #method.set_data_len
check_len(&self) -> Result<()>174     pub fn check_len(&self) -> Result<()> {
175         let data = self.buffer.as_ref();
176         let len = data.len();
177 
178         if len < field::MIN_OPT_LEN {
179             Err(Error)
180         } else {
181             let data_range = field::DATA(data[field::LENGTH]);
182             if len < data_range.end {
183                 Err(Error)
184             } else {
185                 match self.option_type() {
186                     Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()),
187                     Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()),
188                     Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()),
189                     Type::Unknown(_) => Ok(()),
190                     _ => Err(Error),
191                 }
192             }
193         }
194     }
195 
196     /// Consume the NDISC option, returning the underlying buffer.
into_inner(self) -> T197     pub fn into_inner(self) -> T {
198         self.buffer
199     }
200 
201     /// Return the option type.
202     #[inline]
option_type(&self) -> Type203     pub fn option_type(&self) -> Type {
204         let data = self.buffer.as_ref();
205         Type::from(data[field::TYPE])
206     }
207 
208     /// Return the length of the data.
209     #[inline]
data_len(&self) -> u8210     pub fn data_len(&self) -> u8 {
211         let data = self.buffer.as_ref();
212         data[field::LENGTH]
213     }
214 }
215 
216 /// Getter methods only relevant for Source/Target Link-layer Address options.
217 impl<T: AsRef<[u8]>> NdiscOption<T> {
218     /// Return the Source/Target Link-layer Address.
219     #[inline]
link_layer_addr(&self) -> RawHardwareAddress220     pub fn link_layer_addr(&self) -> RawHardwareAddress {
221         let len = MAX_HARDWARE_ADDRESS_LEN.min(self.data_len() as usize * 8 - 2);
222         let data = self.buffer.as_ref();
223         RawHardwareAddress::from_bytes(&data[2..len + 2])
224     }
225 }
226 
227 /// Getter methods only relevant for the MTU option.
228 impl<T: AsRef<[u8]>> NdiscOption<T> {
229     /// Return the MTU value.
230     #[inline]
mtu(&self) -> u32231     pub fn mtu(&self) -> u32 {
232         let data = self.buffer.as_ref();
233         NetworkEndian::read_u32(&data[field::MTU])
234     }
235 }
236 
237 /// Getter methods only relevant for the Prefix Information option.
238 impl<T: AsRef<[u8]>> NdiscOption<T> {
239     /// Return the prefix length.
240     #[inline]
prefix_len(&self) -> u8241     pub fn prefix_len(&self) -> u8 {
242         self.buffer.as_ref()[field::PREFIX_LEN]
243     }
244 
245     /// Return the prefix information flags.
246     #[inline]
prefix_flags(&self) -> PrefixInfoFlags247     pub fn prefix_flags(&self) -> PrefixInfoFlags {
248         PrefixInfoFlags::from_bits_truncate(self.buffer.as_ref()[field::FLAGS])
249     }
250 
251     /// Return the valid lifetime of the prefix.
252     #[inline]
valid_lifetime(&self) -> Duration253     pub fn valid_lifetime(&self) -> Duration {
254         let data = self.buffer.as_ref();
255         Duration::from_secs(NetworkEndian::read_u32(&data[field::VALID_LT]) as u64)
256     }
257 
258     /// Return the preferred lifetime of the prefix.
259     #[inline]
preferred_lifetime(&self) -> Duration260     pub fn preferred_lifetime(&self) -> Duration {
261         let data = self.buffer.as_ref();
262         Duration::from_secs(NetworkEndian::read_u32(&data[field::PREF_LT]) as u64)
263     }
264 
265     /// Return the prefix.
266     #[inline]
prefix(&self) -> Ipv6Address267     pub fn prefix(&self) -> Ipv6Address {
268         let data = self.buffer.as_ref();
269         Ipv6Address::from_bytes(&data[field::PREFIX])
270     }
271 }
272 
273 impl<'a, T: AsRef<[u8]> + ?Sized> NdiscOption<&'a T> {
274     /// Return the option data.
275     #[inline]
data(&self) -> &'a [u8]276     pub fn data(&self) -> &'a [u8] {
277         let len = self.data_len();
278         let data = self.buffer.as_ref();
279         &data[field::DATA(len)]
280     }
281 }
282 
283 /// Core setter methods relevant to any type of NDISC option.
284 impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
285     /// Set the option type.
286     #[inline]
set_option_type(&mut self, value: Type)287     pub fn set_option_type(&mut self, value: Type) {
288         let data = self.buffer.as_mut();
289         data[field::TYPE] = value.into();
290     }
291 
292     /// Set the option data length.
293     #[inline]
set_data_len(&mut self, value: u8)294     pub fn set_data_len(&mut self, value: u8) {
295         let data = self.buffer.as_mut();
296         data[field::LENGTH] = value;
297     }
298 }
299 
300 /// Setter methods only relevant for Source/Target Link-layer Address options.
301 impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
302     /// Set the Source/Target Link-layer Address.
303     #[inline]
set_link_layer_addr(&mut self, addr: RawHardwareAddress)304     pub fn set_link_layer_addr(&mut self, addr: RawHardwareAddress) {
305         let data = self.buffer.as_mut();
306         data[2..2 + addr.len()].copy_from_slice(addr.as_bytes())
307     }
308 }
309 
310 /// Setter methods only relevant for the MTU option.
311 impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
312     /// Set the MTU value.
313     #[inline]
set_mtu(&mut self, value: u32)314     pub fn set_mtu(&mut self, value: u32) {
315         let data = self.buffer.as_mut();
316         NetworkEndian::write_u32(&mut data[field::MTU], value);
317     }
318 }
319 
320 /// Setter methods only relevant for the Prefix Information option.
321 impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
322     /// Set the prefix length.
323     #[inline]
set_prefix_len(&mut self, value: u8)324     pub fn set_prefix_len(&mut self, value: u8) {
325         self.buffer.as_mut()[field::PREFIX_LEN] = value;
326     }
327 
328     /// Set the prefix information flags.
329     #[inline]
set_prefix_flags(&mut self, flags: PrefixInfoFlags)330     pub fn set_prefix_flags(&mut self, flags: PrefixInfoFlags) {
331         self.buffer.as_mut()[field::FLAGS] = flags.bits();
332     }
333 
334     /// Set the valid lifetime of the prefix.
335     #[inline]
set_valid_lifetime(&mut self, time: Duration)336     pub fn set_valid_lifetime(&mut self, time: Duration) {
337         let data = self.buffer.as_mut();
338         NetworkEndian::write_u32(&mut data[field::VALID_LT], time.secs() as u32);
339     }
340 
341     /// Set the preferred lifetime of the prefix.
342     #[inline]
set_preferred_lifetime(&mut self, time: Duration)343     pub fn set_preferred_lifetime(&mut self, time: Duration) {
344         let data = self.buffer.as_mut();
345         NetworkEndian::write_u32(&mut data[field::PREF_LT], time.secs() as u32);
346     }
347 
348     /// Clear the reserved bits.
349     #[inline]
clear_prefix_reserved(&mut self)350     pub fn clear_prefix_reserved(&mut self) {
351         let data = self.buffer.as_mut();
352         NetworkEndian::write_u32(&mut data[field::PREF_RESERVED], 0);
353     }
354 
355     /// Set the prefix.
356     #[inline]
set_prefix(&mut self, addr: Ipv6Address)357     pub fn set_prefix(&mut self, addr: Ipv6Address) {
358         let data = self.buffer.as_mut();
359         data[field::PREFIX].copy_from_slice(addr.as_bytes());
360     }
361 }
362 
363 /// Setter methods only relevant for the Redirected Header option.
364 impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
365     /// Clear the reserved bits.
366     #[inline]
clear_redirected_reserved(&mut self)367     pub fn clear_redirected_reserved(&mut self) {
368         let data = self.buffer.as_mut();
369         data[field::REDIRECTED_RESERVED].fill_with(|| 0);
370     }
371 }
372 
373 impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
374     /// Return a mutable pointer to the option data.
375     #[inline]
data_mut(&mut self) -> &mut [u8]376     pub fn data_mut(&mut self) -> &mut [u8] {
377         let len = self.data_len();
378         let data = self.buffer.as_mut();
379         &mut data[field::DATA(len)]
380     }
381 }
382 
383 impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result384     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385         match Repr::parse(self) {
386             Ok(repr) => write!(f, "{repr}"),
387             Err(err) => {
388                 write!(f, "NDISC Option ({err})")?;
389                 Ok(())
390             }
391         }
392     }
393 }
394 
395 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
396 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
397 pub struct PrefixInformation {
398     pub prefix_len: u8,
399     pub flags: PrefixInfoFlags,
400     pub valid_lifetime: Duration,
401     pub preferred_lifetime: Duration,
402     pub prefix: Ipv6Address,
403 }
404 
405 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
406 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
407 pub struct RedirectedHeader<'a> {
408     pub header: Ipv6Repr,
409     pub data: &'a [u8],
410 }
411 
412 /// A high-level representation of an NDISC Option.
413 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
414 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
415 pub enum Repr<'a> {
416     SourceLinkLayerAddr(RawHardwareAddress),
417     TargetLinkLayerAddr(RawHardwareAddress),
418     PrefixInformation(PrefixInformation),
419     RedirectedHeader(RedirectedHeader<'a>),
420     Mtu(u32),
421     Unknown {
422         type_: u8,
423         length: u8,
424         data: &'a [u8],
425     },
426 }
427 
428 impl<'a> Repr<'a> {
429     /// Parse an NDISC Option and return a high-level representation.
parse<T>(opt: &NdiscOption<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized,430     pub fn parse<T>(opt: &NdiscOption<&'a T>) -> Result<Repr<'a>>
431     where
432         T: AsRef<[u8]> + ?Sized,
433     {
434         match opt.option_type() {
435             Type::SourceLinkLayerAddr => {
436                 if opt.data_len() >= 1 {
437                     Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr()))
438                 } else {
439                     Err(Error)
440                 }
441             }
442             Type::TargetLinkLayerAddr => {
443                 if opt.data_len() >= 1 {
444                     Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
445                 } else {
446                     Err(Error)
447                 }
448             }
449             Type::PrefixInformation => {
450                 if opt.data_len() == 4 {
451                     Ok(Repr::PrefixInformation(PrefixInformation {
452                         prefix_len: opt.prefix_len(),
453                         flags: opt.prefix_flags(),
454                         valid_lifetime: opt.valid_lifetime(),
455                         preferred_lifetime: opt.preferred_lifetime(),
456                         prefix: opt.prefix(),
457                     }))
458                 } else {
459                     Err(Error)
460                 }
461             }
462             Type::RedirectedHeader => {
463                 // If the options data length is less than 6, the option
464                 // does not have enough data to fill out the IP header
465                 // and common option fields.
466                 if opt.data_len() < 6 {
467                     Err(Error)
468                 } else {
469                     let ip_packet =
470                         Ipv6Packet::new_unchecked(&opt.data()[field::REDIRECTED_RESERVED.len()..]);
471                     let ip_repr = Ipv6Repr::parse(&ip_packet)?;
472                     Ok(Repr::RedirectedHeader(RedirectedHeader {
473                         header: ip_repr,
474                         data: &opt.data()
475                             [field::REDIRECTED_RESERVED.len() + ip_repr.buffer_len()..],
476                     }))
477                 }
478             }
479             Type::Mtu => {
480                 if opt.data_len() == 1 {
481                     Ok(Repr::Mtu(opt.mtu()))
482                 } else {
483                     Err(Error)
484                 }
485             }
486             Type::Unknown(id) => {
487                 // A length of 0 is invalid.
488                 if opt.data_len() != 0 {
489                     Ok(Repr::Unknown {
490                         type_: id,
491                         length: opt.data_len(),
492                         data: opt.data(),
493                     })
494                 } else {
495                     Err(Error)
496                 }
497             }
498         }
499     }
500 
501     /// Return the length of a header that will be emitted from this high-level representation.
buffer_len(&self) -> usize502     pub const fn buffer_len(&self) -> usize {
503         match self {
504             &Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => {
505                 let len = 2 + addr.len();
506                 // Round up to next multiple of 8
507                 (len + 7) / 8 * 8
508             }
509             &Repr::PrefixInformation(_) => field::PREFIX.end,
510             &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
511                 (8 + header.buffer_len() + data.len() + 7) / 8 * 8
512             }
513             &Repr::Mtu(_) => field::MTU.end,
514             &Repr::Unknown { length, .. } => field::DATA(length).end,
515         }
516     }
517 
518     /// Emit a high-level representation into an NDISC Option.
emit<T>(&self, opt: &mut NdiscOption<&'a mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,519     pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>)
520     where
521         T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
522     {
523         match *self {
524             Repr::SourceLinkLayerAddr(addr) => {
525                 opt.set_option_type(Type::SourceLinkLayerAddr);
526                 let opt_len = addr.len() + 2;
527                 opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
528                 opt.set_link_layer_addr(addr);
529             }
530             Repr::TargetLinkLayerAddr(addr) => {
531                 opt.set_option_type(Type::TargetLinkLayerAddr);
532                 let opt_len = addr.len() + 2;
533                 opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
534                 opt.set_link_layer_addr(addr);
535             }
536             Repr::PrefixInformation(PrefixInformation {
537                 prefix_len,
538                 flags,
539                 valid_lifetime,
540                 preferred_lifetime,
541                 prefix,
542             }) => {
543                 opt.clear_prefix_reserved();
544                 opt.set_option_type(Type::PrefixInformation);
545                 opt.set_data_len(4);
546                 opt.set_prefix_len(prefix_len);
547                 opt.set_prefix_flags(flags);
548                 opt.set_valid_lifetime(valid_lifetime);
549                 opt.set_preferred_lifetime(preferred_lifetime);
550                 opt.set_prefix(prefix);
551             }
552             Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
553                 // TODO(thvdveld): I think we need to check if the data we are sending is not
554                 // exceeding the MTU.
555                 opt.clear_redirected_reserved();
556                 opt.set_option_type(Type::RedirectedHeader);
557                 opt.set_data_len((((8 + header.buffer_len() + data.len()) + 7) / 8) as u8);
558                 let mut packet = &mut opt.data_mut()[field::REDIRECTED_RESERVED.end - 2..];
559                 let mut ip_packet = Ipv6Packet::new_unchecked(&mut packet);
560                 header.emit(&mut ip_packet);
561                 ip_packet.payload_mut().copy_from_slice(data);
562             }
563             Repr::Mtu(mtu) => {
564                 opt.set_option_type(Type::Mtu);
565                 opt.set_data_len(1);
566                 opt.set_mtu(mtu);
567             }
568             Repr::Unknown {
569                 type_: id,
570                 length,
571                 data,
572             } => {
573                 opt.set_option_type(Type::Unknown(id));
574                 opt.set_data_len(length);
575                 opt.data_mut().copy_from_slice(data);
576             }
577         }
578     }
579 }
580 
581 impl<'a> fmt::Display for Repr<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result582     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
583         write!(f, "NDISC Option: ")?;
584         match *self {
585             Repr::SourceLinkLayerAddr(addr) => {
586                 write!(f, "SourceLinkLayer addr={addr}")
587             }
588             Repr::TargetLinkLayerAddr(addr) => {
589                 write!(f, "TargetLinkLayer addr={addr}")
590             }
591             Repr::PrefixInformation(PrefixInformation {
592                 prefix, prefix_len, ..
593             }) => {
594                 write!(f, "PrefixInformation prefix={prefix}/{prefix_len}")
595             }
596             Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
597                 write!(f, "RedirectedHeader header={header}")
598             }
599             Repr::Mtu(mtu) => {
600                 write!(f, "MTU mtu={mtu}")
601             }
602             Repr::Unknown {
603                 type_: id, length, ..
604             } => {
605                 write!(f, "Unknown({id}) length={length}")
606             }
607         }
608     }
609 }
610 
611 use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
612 
613 impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
pretty_print( buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, indent: &mut PrettyIndent, ) -> fmt::Result614     fn pretty_print(
615         buffer: &dyn AsRef<[u8]>,
616         f: &mut fmt::Formatter,
617         indent: &mut PrettyIndent,
618     ) -> fmt::Result {
619         match NdiscOption::new_checked(buffer) {
620             Err(err) => write!(f, "{indent}({err})"),
621             Ok(ndisc) => match Repr::parse(&ndisc) {
622                 Err(_) => Ok(()),
623                 Ok(repr) => {
624                     write!(f, "{indent}{repr}")
625                 }
626             },
627         }
628     }
629 }
630 
631 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
632 #[cfg(test)]
633 mod test {
634     use super::Error;
635     use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type};
636     use crate::time::Duration;
637     use crate::wire::Ipv6Address;
638 
639     #[cfg(feature = "medium-ethernet")]
640     use crate::wire::EthernetAddress;
641     #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
642     use crate::wire::Ieee802154Address;
643 
644     static PREFIX_OPT_BYTES: [u8; 32] = [
645         0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
646         0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647         0x00, 0x01,
648     ];
649 
650     #[test]
test_deconstruct()651     fn test_deconstruct() {
652         let opt = NdiscOption::new_unchecked(&PREFIX_OPT_BYTES[..]);
653         assert_eq!(opt.option_type(), Type::PrefixInformation);
654         assert_eq!(opt.data_len(), 4);
655         assert_eq!(opt.prefix_len(), 64);
656         assert_eq!(
657             opt.prefix_flags(),
658             PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF
659         );
660         assert_eq!(opt.valid_lifetime(), Duration::from_secs(900));
661         assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000));
662         assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
663     }
664 
665     #[test]
test_construct()666     fn test_construct() {
667         let mut bytes = [0x00; 32];
668         let mut opt = NdiscOption::new_unchecked(&mut bytes[..]);
669         opt.set_option_type(Type::PrefixInformation);
670         opt.set_data_len(4);
671         opt.set_prefix_len(64);
672         opt.set_prefix_flags(PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF);
673         opt.set_valid_lifetime(Duration::from_secs(900));
674         opt.set_preferred_lifetime(Duration::from_secs(1000));
675         opt.set_prefix(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
676         assert_eq!(&PREFIX_OPT_BYTES[..], &*opt.into_inner());
677     }
678 
679     #[test]
test_short_packet()680     fn test_short_packet() {
681         assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error));
682         let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
683         assert_eq!(NdiscOption::new_checked(&bytes), Err(Error));
684     }
685 
686     #[cfg(feature = "medium-ethernet")]
687     #[test]
test_repr_parse_link_layer_opt_ethernet()688     fn test_repr_parse_link_layer_opt_ethernet() {
689         let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34];
690         let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
691         {
692             assert_eq!(
693                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
694                 Ok(Repr::SourceLinkLayerAddr(addr.into()))
695             );
696         }
697         bytes[0] = 0x02;
698         {
699             assert_eq!(
700                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
701                 Ok(Repr::TargetLinkLayerAddr(addr.into()))
702             );
703         }
704     }
705 
706     #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
707     #[test]
test_repr_parse_link_layer_opt_ieee802154()708     fn test_repr_parse_link_layer_opt_ieee802154() {
709         let mut bytes = [
710             0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
711             0x00, 0x00,
712         ];
713         let addr = Ieee802154Address::Extended([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
714         {
715             assert_eq!(
716                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
717                 Ok(Repr::SourceLinkLayerAddr(addr.into()))
718             );
719         }
720         bytes[0] = 0x02;
721         {
722             assert_eq!(
723                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
724                 Ok(Repr::TargetLinkLayerAddr(addr.into()))
725             );
726         }
727     }
728 
729     #[test]
test_repr_parse_prefix_info()730     fn test_repr_parse_prefix_info() {
731         let repr = Repr::PrefixInformation(PrefixInformation {
732             prefix_len: 64,
733             flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
734             valid_lifetime: Duration::from_secs(900),
735             preferred_lifetime: Duration::from_secs(1000),
736             prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
737         });
738         assert_eq!(
739             Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)),
740             Ok(repr)
741         );
742     }
743 
744     #[test]
test_repr_emit_prefix_info()745     fn test_repr_emit_prefix_info() {
746         let mut bytes = [0x2a; 32];
747         let repr = Repr::PrefixInformation(PrefixInformation {
748             prefix_len: 64,
749             flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
750             valid_lifetime: Duration::from_secs(900),
751             preferred_lifetime: Duration::from_secs(1000),
752             prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
753         });
754         let mut opt = NdiscOption::new_unchecked(&mut bytes);
755         repr.emit(&mut opt);
756         assert_eq!(&opt.into_inner()[..], &PREFIX_OPT_BYTES[..]);
757     }
758 
759     #[test]
test_repr_parse_mtu()760     fn test_repr_parse_mtu() {
761         let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc];
762         assert_eq!(
763             Repr::parse(&NdiscOption::new_unchecked(&bytes)),
764             Ok(Repr::Mtu(1500))
765         );
766     }
767 }
768