ipv6option.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. use super::{Error, Result};
  2. #[cfg(feature = "proto-rpl")]
  3. use super::{RplHopByHopPacket, RplHopByHopRepr};
  4. use byteorder::{ByteOrder, NetworkEndian};
  5. use core::fmt;
  6. enum_with_unknown! {
  7. /// IPv6 Extension Header Option Type
  8. pub enum Type(u8) {
  9. /// 1 byte of padding
  10. Pad1 = 0,
  11. /// Multiple bytes of padding
  12. PadN = 1,
  13. /// Router Alert
  14. RouterAlert = 5,
  15. /// RPL Option
  16. Rpl = 0x63,
  17. }
  18. }
  19. impl fmt::Display for Type {
  20. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  21. match *self {
  22. Type::Pad1 => write!(f, "Pad1"),
  23. Type::PadN => write!(f, "PadN"),
  24. Type::Rpl => write!(f, "RPL"),
  25. Type::RouterAlert => write!(f, "RouterAlert"),
  26. Type::Unknown(id) => write!(f, "{id}"),
  27. }
  28. }
  29. }
  30. enum_with_unknown! {
  31. /// A high-level representation of an IPv6 Router Alert Header Option.
  32. ///
  33. /// Router Alert options always contain exactly one `u16`; see [RFC 2711 § 2.1].
  34. ///
  35. /// [RFC 2711 § 2.1]: https://tools.ietf.org/html/rfc2711#section-2.1
  36. pub enum RouterAlert(u16) {
  37. MulticastListenerDiscovery = 0,
  38. Rsvp = 1,
  39. ActiveNetworks = 2,
  40. }
  41. }
  42. impl RouterAlert {
  43. /// Per [RFC 2711 § 2.1], Router Alert options always have 2 bytes of data.
  44. ///
  45. /// [RFC 2711 § 2.1]: https://tools.ietf.org/html/rfc2711#section-2.1
  46. pub const DATA_LEN: u8 = 2;
  47. }
  48. /// Action required when parsing the given IPv6 Extension
  49. /// Header Option Type fails
  50. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
  51. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  52. pub enum FailureType {
  53. /// Skip this option and continue processing the packet
  54. Skip = 0b00000000,
  55. /// Discard the containing packet
  56. Discard = 0b01000000,
  57. /// Discard the containing packet and notify the sender
  58. DiscardSendAll = 0b10000000,
  59. /// Discard the containing packet and only notify the sender
  60. /// if the sender is a unicast address
  61. DiscardSendUnicast = 0b11000000,
  62. }
  63. impl From<u8> for FailureType {
  64. fn from(value: u8) -> FailureType {
  65. match value & 0b11000000 {
  66. 0b00000000 => FailureType::Skip,
  67. 0b01000000 => FailureType::Discard,
  68. 0b10000000 => FailureType::DiscardSendAll,
  69. 0b11000000 => FailureType::DiscardSendUnicast,
  70. _ => unreachable!(),
  71. }
  72. }
  73. }
  74. impl From<FailureType> for u8 {
  75. fn from(value: FailureType) -> Self {
  76. match value {
  77. FailureType::Skip => 0b00000000,
  78. FailureType::Discard => 0b01000000,
  79. FailureType::DiscardSendAll => 0b10000000,
  80. FailureType::DiscardSendUnicast => 0b11000000,
  81. }
  82. }
  83. }
  84. impl fmt::Display for FailureType {
  85. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  86. match *self {
  87. FailureType::Skip => write!(f, "skip"),
  88. FailureType::Discard => write!(f, "discard"),
  89. FailureType::DiscardSendAll => write!(f, "discard and send error"),
  90. FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"),
  91. }
  92. }
  93. }
  94. impl From<Type> for FailureType {
  95. fn from(other: Type) -> FailureType {
  96. let raw: u8 = other.into();
  97. Self::from(raw & 0b11000000u8)
  98. }
  99. }
  100. /// A read/write wrapper around an IPv6 Extension Header Option.
  101. #[derive(Debug, PartialEq, Eq)]
  102. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  103. pub struct Ipv6Option<T: AsRef<[u8]>> {
  104. buffer: T,
  105. }
  106. // Format of Option
  107. //
  108. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - -
  109. // | Option Type | Opt Data Len | Option Data
  110. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - -
  111. //
  112. //
  113. // See https://tools.ietf.org/html/rfc8200#section-4.2 for details.
  114. mod field {
  115. #![allow(non_snake_case)]
  116. use crate::wire::field::*;
  117. // 8-bit identifier of the type of option.
  118. pub const TYPE: usize = 0;
  119. // 8-bit unsigned integer. Length of the DATA field of this option, in octets.
  120. pub const LENGTH: usize = 1;
  121. // Variable-length field. Option-Type-specific data.
  122. pub const fn DATA(length: u8) -> Field {
  123. 2..length as usize + 2
  124. }
  125. }
  126. impl<T: AsRef<[u8]>> Ipv6Option<T> {
  127. /// Create a raw octet buffer with an IPv6 Extension Header Option structure.
  128. pub const fn new_unchecked(buffer: T) -> Ipv6Option<T> {
  129. Ipv6Option { buffer }
  130. }
  131. /// Shorthand for a combination of [new_unchecked] and [check_len].
  132. ///
  133. /// [new_unchecked]: #method.new_unchecked
  134. /// [check_len]: #method.check_len
  135. pub fn new_checked(buffer: T) -> Result<Ipv6Option<T>> {
  136. let opt = Self::new_unchecked(buffer);
  137. opt.check_len()?;
  138. Ok(opt)
  139. }
  140. /// Ensure that no accessor method will panic if called.
  141. /// Returns `Err(Error)` if the buffer is too short.
  142. ///
  143. /// The result of this check is invalidated by calling [set_data_len].
  144. ///
  145. /// [set_data_len]: #method.set_data_len
  146. pub fn check_len(&self) -> Result<()> {
  147. let data = self.buffer.as_ref();
  148. let len = data.len();
  149. if len < field::LENGTH {
  150. return Err(Error);
  151. }
  152. if self.option_type() == Type::Pad1 {
  153. return Ok(());
  154. }
  155. if len == field::LENGTH {
  156. return Err(Error);
  157. }
  158. let df = field::DATA(data[field::LENGTH]);
  159. if len < df.end {
  160. return Err(Error);
  161. }
  162. Ok(())
  163. }
  164. /// Consume the ipv6 option, returning the underlying buffer.
  165. pub fn into_inner(self) -> T {
  166. self.buffer
  167. }
  168. /// Return the option type.
  169. #[inline]
  170. pub fn option_type(&self) -> Type {
  171. let data = self.buffer.as_ref();
  172. Type::from(data[field::TYPE])
  173. }
  174. /// Return the length of the data.
  175. ///
  176. /// # Panics
  177. /// This function panics if this is an 1-byte padding option.
  178. #[inline]
  179. pub fn data_len(&self) -> u8 {
  180. let data = self.buffer.as_ref();
  181. data[field::LENGTH]
  182. }
  183. }
  184. impl<'a, T: AsRef<[u8]> + ?Sized> Ipv6Option<&'a T> {
  185. /// Return the option data.
  186. ///
  187. /// # Panics
  188. /// This function panics if this is an 1-byte padding option.
  189. #[inline]
  190. pub fn data(&self) -> &'a [u8] {
  191. let len = self.data_len();
  192. let data = self.buffer.as_ref();
  193. &data[field::DATA(len)]
  194. }
  195. }
  196. impl<T: AsRef<[u8]> + AsMut<[u8]>> Ipv6Option<T> {
  197. /// Set the option type.
  198. #[inline]
  199. pub fn set_option_type(&mut self, value: Type) {
  200. let data = self.buffer.as_mut();
  201. data[field::TYPE] = value.into();
  202. }
  203. /// Set the option data length.
  204. ///
  205. /// # Panics
  206. /// This function panics if this is an 1-byte padding option.
  207. #[inline]
  208. pub fn set_data_len(&mut self, value: u8) {
  209. let data = self.buffer.as_mut();
  210. data[field::LENGTH] = value;
  211. }
  212. }
  213. impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Ipv6Option<&'a mut T> {
  214. /// Return a mutable pointer to the option data.
  215. ///
  216. /// # Panics
  217. /// This function panics if this is an 1-byte padding option.
  218. #[inline]
  219. pub fn data_mut(&mut self) -> &mut [u8] {
  220. let len = self.data_len();
  221. let data = self.buffer.as_mut();
  222. &mut data[field::DATA(len)]
  223. }
  224. }
  225. impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> {
  226. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  227. match Repr::parse(self) {
  228. Ok(repr) => write!(f, "{repr}"),
  229. Err(err) => {
  230. write!(f, "IPv6 Extension Option ({err})")?;
  231. Ok(())
  232. }
  233. }
  234. }
  235. }
  236. /// A high-level representation of an IPv6 Extension Header Option.
  237. #[derive(Debug, PartialEq, Eq, Clone, Copy)]
  238. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  239. #[non_exhaustive]
  240. pub enum Repr<'a> {
  241. Pad1,
  242. PadN(u8),
  243. RouterAlert(RouterAlert),
  244. #[cfg(feature = "proto-rpl")]
  245. Rpl(RplHopByHopRepr),
  246. Unknown {
  247. type_: Type,
  248. length: u8,
  249. data: &'a [u8],
  250. },
  251. }
  252. impl<'a> Repr<'a> {
  253. /// Parse an IPv6 Extension Header Option and return a high-level representation.
  254. pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>>
  255. where
  256. T: AsRef<[u8]> + ?Sized,
  257. {
  258. opt.check_len()?;
  259. match opt.option_type() {
  260. Type::Pad1 => Ok(Repr::Pad1),
  261. Type::PadN => Ok(Repr::PadN(opt.data_len())),
  262. Type::RouterAlert => {
  263. if opt.data_len() == RouterAlert::DATA_LEN {
  264. let raw = NetworkEndian::read_u16(opt.data());
  265. Ok(Repr::RouterAlert(RouterAlert::from(raw)))
  266. } else {
  267. Err(Error)
  268. }
  269. }
  270. #[cfg(feature = "proto-rpl")]
  271. Type::Rpl => Ok(Repr::Rpl(RplHopByHopRepr::parse(
  272. &RplHopByHopPacket::new_checked(opt.data())?,
  273. ))),
  274. #[cfg(not(feature = "proto-rpl"))]
  275. Type::Rpl => Ok(Repr::Unknown {
  276. type_: Type::Rpl,
  277. length: opt.data_len(),
  278. data: opt.data(),
  279. }),
  280. unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown {
  281. type_: unknown_type,
  282. length: opt.data_len(),
  283. data: opt.data(),
  284. }),
  285. }
  286. }
  287. /// Return the length of a header that will be emitted from this high-level representation.
  288. pub const fn buffer_len(&self) -> usize {
  289. match *self {
  290. Repr::Pad1 => 1,
  291. Repr::PadN(length) => field::DATA(length).end,
  292. Repr::RouterAlert(_) => field::DATA(RouterAlert::DATA_LEN).end,
  293. #[cfg(feature = "proto-rpl")]
  294. Repr::Rpl(opt) => field::DATA(opt.buffer_len() as u8).end,
  295. Repr::Unknown { length, .. } => field::DATA(length).end,
  296. }
  297. }
  298. /// Emit a high-level representation into an IPv6 Extension Header Option.
  299. pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) {
  300. match *self {
  301. Repr::Pad1 => opt.set_option_type(Type::Pad1),
  302. Repr::PadN(len) => {
  303. opt.set_option_type(Type::PadN);
  304. opt.set_data_len(len);
  305. // Ensure all padding bytes are set to zero.
  306. for x in opt.data_mut().iter_mut() {
  307. *x = 0
  308. }
  309. }
  310. Repr::RouterAlert(router_alert) => {
  311. opt.set_option_type(Type::RouterAlert);
  312. opt.set_data_len(RouterAlert::DATA_LEN);
  313. NetworkEndian::write_u16(opt.data_mut(), router_alert.into());
  314. }
  315. #[cfg(feature = "proto-rpl")]
  316. Repr::Rpl(rpl) => {
  317. opt.set_option_type(Type::Rpl);
  318. opt.set_data_len(4);
  319. rpl.emit(&mut crate::wire::RplHopByHopPacket::new_unchecked(
  320. opt.data_mut(),
  321. ));
  322. }
  323. Repr::Unknown {
  324. type_,
  325. length,
  326. data,
  327. } => {
  328. opt.set_option_type(type_);
  329. opt.set_data_len(length);
  330. opt.data_mut().copy_from_slice(&data[..length as usize]);
  331. }
  332. }
  333. }
  334. }
  335. /// A iterator for IPv6 options.
  336. #[derive(Debug)]
  337. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  338. pub struct Ipv6OptionsIterator<'a> {
  339. pos: usize,
  340. length: usize,
  341. data: &'a [u8],
  342. hit_error: bool,
  343. }
  344. impl<'a> Ipv6OptionsIterator<'a> {
  345. /// Create a new `Ipv6OptionsIterator`, used to iterate over the
  346. /// options contained in a IPv6 Extension Header (e.g. the Hop-by-Hop
  347. /// header).
  348. pub fn new(data: &'a [u8]) -> Ipv6OptionsIterator<'a> {
  349. let length = data.len();
  350. Ipv6OptionsIterator {
  351. pos: 0,
  352. hit_error: false,
  353. length,
  354. data,
  355. }
  356. }
  357. }
  358. impl<'a> Iterator for Ipv6OptionsIterator<'a> {
  359. type Item = Result<Repr<'a>>;
  360. fn next(&mut self) -> Option<Self::Item> {
  361. if self.pos < self.length && !self.hit_error {
  362. // If we still have data to parse and we have not previously
  363. // hit an error, attempt to parse the next option.
  364. match Ipv6Option::new_checked(&self.data[self.pos..]) {
  365. Ok(hdr) => match Repr::parse(&hdr) {
  366. Ok(repr) => {
  367. self.pos += repr.buffer_len();
  368. Some(Ok(repr))
  369. }
  370. Err(e) => {
  371. self.hit_error = true;
  372. Some(Err(e))
  373. }
  374. },
  375. Err(e) => {
  376. self.hit_error = true;
  377. Some(Err(e))
  378. }
  379. }
  380. } else {
  381. // If we failed to parse a previous option or hit the end of the
  382. // buffer, we do not continue to iterate.
  383. None
  384. }
  385. }
  386. }
  387. impl<'a> fmt::Display for Repr<'a> {
  388. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  389. write!(f, "IPv6 Option ")?;
  390. match *self {
  391. Repr::Pad1 => write!(f, "{} ", Type::Pad1),
  392. Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
  393. Repr::RouterAlert(alert) => write!(f, "{} value={:?}", Type::RouterAlert, alert),
  394. #[cfg(feature = "proto-rpl")]
  395. Repr::Rpl(rpl) => write!(f, "{} {rpl}", Type::Rpl),
  396. Repr::Unknown { type_, length, .. } => write!(f, "{type_} length={length} "),
  397. }
  398. }
  399. }
  400. #[cfg(test)]
  401. mod test {
  402. use super::*;
  403. static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
  404. static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
  405. static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0];
  406. static IPV6OPTION_BYTES_ROUTER_ALERT_MLD: [u8; 4] = [0x05, 0x02, 0x00, 0x00];
  407. static IPV6OPTION_BYTES_ROUTER_ALERT_RSVP: [u8; 4] = [0x05, 0x02, 0x00, 0x01];
  408. static IPV6OPTION_BYTES_ROUTER_ALERT_ACTIVE_NETWORKS: [u8; 4] = [0x05, 0x02, 0x00, 0x02];
  409. static IPV6OPTION_BYTES_ROUTER_ALERT_UNKNOWN: [u8; 4] = [0x05, 0x02, 0xbe, 0xef];
  410. #[cfg(feature = "proto-rpl")]
  411. static IPV6OPTION_BYTES_RPL: [u8; 6] = [0x63, 0x04, 0x00, 0x1e, 0x08, 0x00];
  412. #[test]
  413. fn test_check_len() {
  414. let bytes = [0u8];
  415. // zero byte buffer
  416. assert_eq!(
  417. Err(Error),
  418. Ipv6Option::new_unchecked(&bytes[..0]).check_len()
  419. );
  420. // pad1
  421. assert_eq!(
  422. Ok(()),
  423. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()
  424. );
  425. // padn with truncated data
  426. assert_eq!(
  427. Err(Error),
  428. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()
  429. );
  430. // padn
  431. assert_eq!(
  432. Ok(()),
  433. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
  434. );
  435. // router alert with truncated data
  436. assert_eq!(
  437. Err(Error),
  438. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD[..3]).check_len()
  439. );
  440. // router alert
  441. assert_eq!(
  442. Ok(()),
  443. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD).check_len()
  444. );
  445. // unknown option type with truncated data
  446. assert_eq!(
  447. Err(Error),
  448. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()
  449. );
  450. assert_eq!(
  451. Err(Error),
  452. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()
  453. );
  454. // unknown type
  455. assert_eq!(
  456. Ok(()),
  457. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()
  458. );
  459. #[cfg(feature = "proto-rpl")]
  460. {
  461. assert_eq!(
  462. Ok(()),
  463. Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL).check_len()
  464. );
  465. }
  466. }
  467. #[test]
  468. #[should_panic(expected = "index out of bounds")]
  469. fn test_data_len() {
  470. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
  471. opt.data_len();
  472. }
  473. #[test]
  474. fn test_option_deconstruct() {
  475. // one octet of padding
  476. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
  477. assert_eq!(opt.option_type(), Type::Pad1);
  478. // two octets of padding
  479. let bytes: [u8; 2] = [0x1, 0x0];
  480. let opt = Ipv6Option::new_unchecked(&bytes);
  481. assert_eq!(opt.option_type(), Type::PadN);
  482. assert_eq!(opt.data_len(), 0);
  483. // three octets of padding
  484. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN);
  485. assert_eq!(opt.option_type(), Type::PadN);
  486. assert_eq!(opt.data_len(), 1);
  487. assert_eq!(opt.data(), &[0]);
  488. // extra bytes in buffer
  489. let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
  490. let opt = Ipv6Option::new_unchecked(&bytes);
  491. assert_eq!(opt.option_type(), Type::PadN);
  492. assert_eq!(opt.data_len(), 7);
  493. assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]);
  494. // router alert
  495. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
  496. assert_eq!(opt.option_type(), Type::RouterAlert);
  497. assert_eq!(opt.data_len(), 2);
  498. assert_eq!(opt.data(), &[0, 0]);
  499. // unrecognized option
  500. let bytes: [u8; 1] = [0xff];
  501. let opt = Ipv6Option::new_unchecked(&bytes);
  502. assert_eq!(opt.option_type(), Type::Unknown(255));
  503. // unrecognized option without length and data
  504. assert_eq!(Ipv6Option::new_checked(&bytes), Err(Error));
  505. #[cfg(feature = "proto-rpl")]
  506. {
  507. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
  508. assert_eq!(opt.option_type(), Type::Rpl);
  509. assert_eq!(opt.data_len(), 4);
  510. assert_eq!(opt.data(), &[0x00, 0x1e, 0x08, 0x00]);
  511. }
  512. }
  513. #[test]
  514. fn test_option_parse() {
  515. // one octet of padding
  516. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1);
  517. let pad1 = Repr::parse(&opt).unwrap();
  518. assert_eq!(pad1, Repr::Pad1);
  519. assert_eq!(pad1.buffer_len(), 1);
  520. // two or more octets of padding
  521. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN);
  522. let padn = Repr::parse(&opt).unwrap();
  523. assert_eq!(padn, Repr::PadN(1));
  524. assert_eq!(padn.buffer_len(), 3);
  525. // router alert (MLD)
  526. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
  527. let alert = Repr::parse(&opt).unwrap();
  528. assert_eq!(
  529. alert,
  530. Repr::RouterAlert(RouterAlert::MulticastListenerDiscovery)
  531. );
  532. assert_eq!(alert.buffer_len(), 4);
  533. // router alert (RSVP)
  534. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_RSVP);
  535. let alert = Repr::parse(&opt).unwrap();
  536. assert_eq!(alert, Repr::RouterAlert(RouterAlert::Rsvp));
  537. assert_eq!(alert.buffer_len(), 4);
  538. // router alert (active networks)
  539. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_ACTIVE_NETWORKS);
  540. let alert = Repr::parse(&opt).unwrap();
  541. assert_eq!(alert, Repr::RouterAlert(RouterAlert::ActiveNetworks));
  542. assert_eq!(alert.buffer_len(), 4);
  543. // router alert (unknown)
  544. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_UNKNOWN);
  545. let alert = Repr::parse(&opt).unwrap();
  546. assert_eq!(alert, Repr::RouterAlert(RouterAlert::Unknown(0xbeef)));
  547. assert_eq!(alert.buffer_len(), 4);
  548. // router alert (incorrect data length)
  549. let opt = Ipv6Option::new_unchecked(&[0x05, 0x03, 0x00, 0x00, 0x00]);
  550. let alert = Repr::parse(&opt);
  551. assert_eq!(alert, Err(Error));
  552. // unrecognized option type
  553. let data = [0u8; 3];
  554. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
  555. let unknown = Repr::parse(&opt).unwrap();
  556. assert_eq!(
  557. unknown,
  558. Repr::Unknown {
  559. type_: Type::Unknown(255),
  560. length: 3,
  561. data: &data
  562. }
  563. );
  564. #[cfg(feature = "proto-rpl")]
  565. {
  566. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
  567. let rpl = Repr::parse(&opt).unwrap();
  568. assert_eq!(
  569. rpl,
  570. Repr::Rpl(crate::wire::RplHopByHopRepr {
  571. down: false,
  572. rank_error: false,
  573. forwarding_error: false,
  574. instance_id: crate::wire::RplInstanceId::from(0x1e),
  575. sender_rank: 0x0800,
  576. })
  577. );
  578. }
  579. }
  580. #[test]
  581. fn test_option_emit() {
  582. let repr = Repr::Pad1;
  583. let mut bytes = [255u8; 1]; // don't assume bytes are initialized to zero
  584. let mut opt = Ipv6Option::new_unchecked(&mut bytes);
  585. repr.emit(&mut opt);
  586. assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PAD1);
  587. let repr = Repr::PadN(1);
  588. let mut bytes = [255u8; 3]; // don't assume bytes are initialized to zero
  589. let mut opt = Ipv6Option::new_unchecked(&mut bytes);
  590. repr.emit(&mut opt);
  591. assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
  592. let repr = Repr::RouterAlert(RouterAlert::MulticastListenerDiscovery);
  593. let mut bytes = [255u8; 4]; // don't assume bytes are initialized to zero
  594. let mut opt = Ipv6Option::new_unchecked(&mut bytes);
  595. repr.emit(&mut opt);
  596. assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
  597. let data = [0u8; 3];
  598. let repr = Repr::Unknown {
  599. type_: Type::Unknown(255),
  600. length: 3,
  601. data: &data,
  602. };
  603. let mut bytes = [254u8; 5]; // don't assume bytes are initialized to zero
  604. let mut opt = Ipv6Option::new_unchecked(&mut bytes);
  605. repr.emit(&mut opt);
  606. assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_UNKNOWN);
  607. #[cfg(feature = "proto-rpl")]
  608. {
  609. let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL);
  610. let rpl = Repr::parse(&opt).unwrap();
  611. let mut bytes = [0u8; 6];
  612. rpl.emit(&mut Ipv6Option::new_unchecked(&mut bytes));
  613. assert_eq!(&bytes, &IPV6OPTION_BYTES_RPL);
  614. }
  615. }
  616. #[test]
  617. fn test_failure_type() {
  618. let mut failure_type: FailureType = Type::Pad1.into();
  619. assert_eq!(failure_type, FailureType::Skip);
  620. failure_type = Type::PadN.into();
  621. assert_eq!(failure_type, FailureType::Skip);
  622. failure_type = Type::RouterAlert.into();
  623. assert_eq!(failure_type, FailureType::Skip);
  624. failure_type = Type::Unknown(0b01000001).into();
  625. assert_eq!(failure_type, FailureType::Discard);
  626. failure_type = Type::Unknown(0b10100000).into();
  627. assert_eq!(failure_type, FailureType::DiscardSendAll);
  628. failure_type = Type::Unknown(0b11000100).into();
  629. assert_eq!(failure_type, FailureType::DiscardSendUnicast);
  630. }
  631. #[test]
  632. fn test_options_iter() {
  633. let options = [
  634. 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x05,
  635. 0x02, 0x00, 0x01, 0x01, 0x08, 0x00,
  636. ];
  637. let iterator = Ipv6OptionsIterator::new(&options);
  638. for (i, opt) in iterator.enumerate() {
  639. match (i, opt) {
  640. (0, Ok(Repr::Pad1)) => continue,
  641. (1, Ok(Repr::PadN(1))) => continue,
  642. (2, Ok(Repr::PadN(2))) => continue,
  643. (3, Ok(Repr::PadN(0))) => continue,
  644. (4, Ok(Repr::Pad1)) => continue,
  645. (
  646. 5,
  647. Ok(Repr::Unknown {
  648. type_: Type::Unknown(0x11),
  649. length: 0,
  650. ..
  651. }),
  652. ) => continue,
  653. (6, Ok(Repr::RouterAlert(RouterAlert::Rsvp))) => continue,
  654. (7, Err(Error)) => continue,
  655. (i, res) => panic!("Unexpected option `{res:?}` at index {i}"),
  656. }
  657. }
  658. }
  659. }