|
@@ -2,6 +2,7 @@ use super::{Error, Result};
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
use super::{RplHopByHopPacket, RplHopByHopRepr};
|
|
|
|
|
|
+use byteorder::{ByteOrder, NetworkEndian};
|
|
|
use core::fmt;
|
|
|
|
|
|
enum_with_unknown! {
|
|
@@ -11,6 +12,8 @@ enum_with_unknown! {
|
|
|
Pad1 = 0,
|
|
|
/// Multiple bytes of padding
|
|
|
PadN = 1,
|
|
|
+ /// Router Alert
|
|
|
+ RouterAlert = 5,
|
|
|
/// RPL Option
|
|
|
Rpl = 0x63,
|
|
|
}
|
|
@@ -22,11 +25,32 @@ impl fmt::Display for Type {
|
|
|
Type::Pad1 => write!(f, "Pad1"),
|
|
|
Type::PadN => write!(f, "PadN"),
|
|
|
Type::Rpl => write!(f, "RPL"),
|
|
|
+ Type::RouterAlert => write!(f, "RouterAlert"),
|
|
|
Type::Unknown(id) => write!(f, "{id}"),
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+enum_with_unknown! {
|
|
|
+ /// A high-level representation of an IPv6 Router Alert Header Option.
|
|
|
+ ///
|
|
|
+ /// Router Alert options always contain exactly one `u16`; see [RFC 2711 § 2.1].
|
|
|
+ ///
|
|
|
+ /// [RFC 2711 § 2.1]: https://tools.ietf.org/html/rfc2711#section-2.1
|
|
|
+ pub enum RouterAlert(u16) {
|
|
|
+ MulticastListenerDiscovery = 0,
|
|
|
+ Rsvp = 1,
|
|
|
+ ActiveNetworks = 2,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl RouterAlert {
|
|
|
+ /// Per [RFC 2711 § 2.1], Router Alert options always have 2 bytes of data.
|
|
|
+ ///
|
|
|
+ /// [RFC 2711 § 2.1]: https://tools.ietf.org/html/rfc2711#section-2.1
|
|
|
+ pub const DATA_LEN: u8 = 2;
|
|
|
+}
|
|
|
+
|
|
|
enum_with_unknown! {
|
|
|
/// Action required when parsing the given IPv6 Extension
|
|
|
/// Header Option Type fails
|
|
@@ -226,6 +250,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> {
|
|
|
pub enum Repr<'a> {
|
|
|
Pad1,
|
|
|
PadN(u8),
|
|
|
+ RouterAlert(RouterAlert),
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
Rpl(RplHopByHopRepr),
|
|
|
Unknown {
|
|
@@ -245,7 +270,14 @@ impl<'a> Repr<'a> {
|
|
|
match opt.option_type() {
|
|
|
Type::Pad1 => Ok(Repr::Pad1),
|
|
|
Type::PadN => Ok(Repr::PadN(opt.data_len())),
|
|
|
-
|
|
|
+ Type::RouterAlert => {
|
|
|
+ if opt.data_len() == RouterAlert::DATA_LEN {
|
|
|
+ let raw = NetworkEndian::read_u16(opt.data());
|
|
|
+ Ok(Repr::RouterAlert(RouterAlert::from(raw)))
|
|
|
+ } else {
|
|
|
+ Err(Error)
|
|
|
+ }
|
|
|
+ }
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
Type::Rpl => Ok(Repr::Rpl(RplHopByHopRepr::parse(
|
|
|
&RplHopByHopPacket::new_checked(opt.data())?,
|
|
@@ -270,6 +302,7 @@ impl<'a> Repr<'a> {
|
|
|
match *self {
|
|
|
Repr::Pad1 => 1,
|
|
|
Repr::PadN(length) => field::DATA(length).end,
|
|
|
+ Repr::RouterAlert(_) => field::DATA(RouterAlert::DATA_LEN).end,
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
Repr::Rpl(opt) => field::DATA(opt.buffer_len() as u8).end,
|
|
|
Repr::Unknown { length, .. } => field::DATA(length).end,
|
|
@@ -288,6 +321,11 @@ impl<'a> Repr<'a> {
|
|
|
*x = 0
|
|
|
}
|
|
|
}
|
|
|
+ Repr::RouterAlert(router_alert) => {
|
|
|
+ opt.set_option_type(Type::RouterAlert);
|
|
|
+ opt.set_data_len(RouterAlert::DATA_LEN);
|
|
|
+ NetworkEndian::write_u16(opt.data_mut(), router_alert.into());
|
|
|
+ }
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
Repr::Rpl(rpl) => {
|
|
|
opt.set_option_type(Type::Rpl);
|
|
@@ -371,6 +409,7 @@ impl<'a> fmt::Display for Repr<'a> {
|
|
|
match *self {
|
|
|
Repr::Pad1 => write!(f, "{} ", Type::Pad1),
|
|
|
Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
|
|
|
+ Repr::RouterAlert(alert) => write!(f, "{} value={:?}", Type::RouterAlert, alert),
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
Repr::Rpl(rpl) => write!(f, "{} {rpl}", Type::Rpl),
|
|
|
Repr::Unknown { type_, length, .. } => write!(f, "{type_} length={length} "),
|
|
@@ -385,6 +424,10 @@ mod test {
|
|
|
static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
|
|
|
static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
|
|
|
static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0];
|
|
|
+ static IPV6OPTION_BYTES_ROUTER_ALERT_MLD: [u8; 4] = [0x05, 0x02, 0x00, 0x00];
|
|
|
+ static IPV6OPTION_BYTES_ROUTER_ALERT_RSVP: [u8; 4] = [0x05, 0x02, 0x00, 0x01];
|
|
|
+ static IPV6OPTION_BYTES_ROUTER_ALERT_ACTIVE_NETWORKS: [u8; 4] = [0x05, 0x02, 0x00, 0x02];
|
|
|
+ static IPV6OPTION_BYTES_ROUTER_ALERT_UNKNOWN: [u8; 4] = [0x05, 0x02, 0xbe, 0xef];
|
|
|
#[cfg(feature = "proto-rpl")]
|
|
|
static IPV6OPTION_BYTES_RPL: [u8; 6] = [0x63, 0x04, 0x00, 0x1e, 0x08, 0x00];
|
|
|
|
|
@@ -413,6 +456,17 @@ mod test {
|
|
|
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
|
|
|
);
|
|
|
|
|
|
+ // router alert with truncated data
|
|
|
+ assert_eq!(
|
|
|
+ Err(Error),
|
|
|
+ Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD[..3]).check_len()
|
|
|
+ );
|
|
|
+ // router alert
|
|
|
+ assert_eq!(
|
|
|
+ Ok(()),
|
|
|
+ Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD).check_len()
|
|
|
+ );
|
|
|
+
|
|
|
// unknown option type with truncated data
|
|
|
assert_eq!(
|
|
|
Err(Error),
|
|
@@ -469,6 +523,12 @@ mod test {
|
|
|
assert_eq!(opt.data_len(), 7);
|
|
|
assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]);
|
|
|
|
|
|
+ // router alert
|
|
|
+ let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
|
|
|
+ assert_eq!(opt.option_type(), Type::RouterAlert);
|
|
|
+ assert_eq!(opt.data_len(), 2);
|
|
|
+ assert_eq!(opt.data(), &[0, 0]);
|
|
|
+
|
|
|
// unrecognized option
|
|
|
let bytes: [u8; 1] = [0xff];
|
|
|
let opt = Ipv6Option::new_unchecked(&bytes);
|
|
@@ -500,6 +560,38 @@ mod test {
|
|
|
assert_eq!(padn, Repr::PadN(1));
|
|
|
assert_eq!(padn.buffer_len(), 3);
|
|
|
|
|
|
+ // router alert (MLD)
|
|
|
+ let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
|
|
|
+ let alert = Repr::parse(&opt).unwrap();
|
|
|
+ assert_eq!(
|
|
|
+ alert,
|
|
|
+ Repr::RouterAlert(RouterAlert::MulticastListenerDiscovery)
|
|
|
+ );
|
|
|
+ assert_eq!(alert.buffer_len(), 4);
|
|
|
+
|
|
|
+ // router alert (RSVP)
|
|
|
+ let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_RSVP);
|
|
|
+ let alert = Repr::parse(&opt).unwrap();
|
|
|
+ assert_eq!(alert, Repr::RouterAlert(RouterAlert::Rsvp));
|
|
|
+ assert_eq!(alert.buffer_len(), 4);
|
|
|
+
|
|
|
+ // router alert (active networks)
|
|
|
+ let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_ACTIVE_NETWORKS);
|
|
|
+ let alert = Repr::parse(&opt).unwrap();
|
|
|
+ assert_eq!(alert, Repr::RouterAlert(RouterAlert::ActiveNetworks));
|
|
|
+ assert_eq!(alert.buffer_len(), 4);
|
|
|
+
|
|
|
+ // router alert (unknown)
|
|
|
+ let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_ROUTER_ALERT_UNKNOWN);
|
|
|
+ let alert = Repr::parse(&opt).unwrap();
|
|
|
+ assert_eq!(alert, Repr::RouterAlert(RouterAlert::Unknown(0xbeef)));
|
|
|
+ assert_eq!(alert.buffer_len(), 4);
|
|
|
+
|
|
|
+ // router alert (incorrect data length)
|
|
|
+ let opt = Ipv6Option::new_unchecked(&[0x05, 0x03, 0x00, 0x00, 0x00]);
|
|
|
+ let alert = Repr::parse(&opt);
|
|
|
+ assert_eq!(alert, Err(Error));
|
|
|
+
|
|
|
// unrecognized option type
|
|
|
let data = [0u8; 3];
|
|
|
let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
|
|
@@ -545,6 +637,12 @@ mod test {
|
|
|
repr.emit(&mut opt);
|
|
|
assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
|
|
|
|
|
|
+ let repr = Repr::RouterAlert(RouterAlert::MulticastListenerDiscovery);
|
|
|
+ let mut bytes = [255u8; 4]; // don't assume bytes are initialized to zero
|
|
|
+ let mut opt = Ipv6Option::new_unchecked(&mut bytes);
|
|
|
+ repr.emit(&mut opt);
|
|
|
+ assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_ROUTER_ALERT_MLD);
|
|
|
+
|
|
|
let data = [0u8; 3];
|
|
|
let repr = Repr::Unknown {
|
|
|
type_: Type::Unknown(255),
|
|
@@ -573,6 +671,8 @@ mod test {
|
|
|
assert_eq!(failure_type, FailureType::Skip);
|
|
|
failure_type = Type::PadN.into();
|
|
|
assert_eq!(failure_type, FailureType::Skip);
|
|
|
+ failure_type = Type::RouterAlert.into();
|
|
|
+ assert_eq!(failure_type, FailureType::Skip);
|
|
|
failure_type = Type::Unknown(0b01000001).into();
|
|
|
assert_eq!(failure_type, FailureType::Discard);
|
|
|
failure_type = Type::Unknown(0b10100000).into();
|
|
@@ -584,8 +684,8 @@ mod test {
|
|
|
#[test]
|
|
|
fn test_options_iter() {
|
|
|
let options = [
|
|
|
- 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x01,
|
|
|
- 0x08, 0x00,
|
|
|
+ 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x05,
|
|
|
+ 0x02, 0x00, 0x01, 0x01, 0x08, 0x00,
|
|
|
];
|
|
|
|
|
|
let iterator = Ipv6OptionsIterator::new(&options);
|
|
@@ -604,7 +704,8 @@ mod test {
|
|
|
..
|
|
|
}),
|
|
|
) => continue,
|
|
|
- (6, Err(Error)) => continue,
|
|
|
+ (6, Ok(Repr::RouterAlert(RouterAlert::Rsvp))) => continue,
|
|
|
+ (7, Err(Error)) => continue,
|
|
|
(i, res) => panic!("Unexpected option `{res:?}` at index {i}"),
|
|
|
}
|
|
|
}
|