ndiscoption.rs 27 KB

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