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