mod neighbor_advert;
mod neighbor_solicit;
mod options;
mod redirect;
mod router_advert;
mod router_solicit;
pub use self::neighbor_advert::*;
pub use self::neighbor_solicit::*;
pub use self::options::*;
pub use self::redirect::*;
pub use self::router_advert::*;
pub use self::router_solicit::*;
use crate::dpdk::BufferError;
use crate::packets::{Immutable, Internal, Packet};
use crate::{ensure, Mbuf, SizeOf};
use anyhow::Result;
use std::fmt;
use std::marker::PhantomData;
use std::ptr::NonNull;
pub trait NdpPacket: Packet {
fn options_offset(&self) -> usize;
#[inline]
fn options_iter(&self) -> ImmutableNdpOptionsIterator<'_> {
let mbuf = unsafe { self.mbuf().clone(Internal(())) };
ImmutableNdpOptionsIterator {
mbuf,
offset: self.options_offset(),
_phantom: PhantomData,
}
}
#[inline]
fn options_mut(&mut self) -> NdpOptions<'_> {
let offset = self.options_offset();
NdpOptions {
mbuf: self.mbuf_mut(),
offset,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[repr(C, packed)]
pub struct NdpOptionType(pub u8);
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
pub mod NdpOptionTypes {
use super::NdpOptionType;
pub const SourceLinkLayerAddress: NdpOptionType = NdpOptionType(1);
pub const TargetLinkLayerAddress: NdpOptionType = NdpOptionType(2);
pub const PrefixInformation: NdpOptionType = NdpOptionType(3);
pub const RedirectedHeader: NdpOptionType = NdpOptionType(4);
pub const Mtu: NdpOptionType = NdpOptionType(5);
}
impl fmt::Display for NdpOptionType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match *self {
NdpOptionTypes::SourceLinkLayerAddress => "Source Link-layer Address".to_string(),
NdpOptionTypes::TargetLinkLayerAddress => "Target Link-layer Address".to_string(),
NdpOptionTypes::PrefixInformation => "Prefix Information".to_string(),
NdpOptionTypes::RedirectedHeader => "Redirected Header".to_string(),
NdpOptionTypes::Mtu => "MTU".to_string(),
_ => format!("{}", self.0),
}
)
}
}
#[derive(Clone, Copy, Debug, SizeOf)]
#[repr(C, packed)]
struct TypeLengthTuple {
option_type: u8,
length: u8,
}
pub struct ImmutableNdpOption<'a> {
mbuf: &'a mut Mbuf,
tuple: NonNull<TypeLengthTuple>,
offset: usize,
}
impl<'a> ImmutableNdpOption<'a> {
#[inline]
fn new(mbuf: &'a mut Mbuf, offset: usize) -> Result<Self> {
let tuple = mbuf.read_data(offset)?;
let option = ImmutableNdpOption {
mbuf,
tuple,
offset,
};
ensure!(
option.mbuf.len() >= option.end_offset(),
BufferError::OutOfBuffer(option.end_offset(), option.mbuf.len())
);
Ok(option)
}
#[inline]
fn tuple(&self) -> &TypeLengthTuple {
unsafe { self.tuple.as_ref() }
}
#[inline]
pub fn option_type(&self) -> NdpOptionType {
NdpOptionType(self.tuple().option_type)
}
#[inline]
pub fn length(&self) -> u8 {
self.tuple().length
}
#[inline]
fn end_offset(&self) -> usize {
self.offset + self.length() as usize * 8
}
#[inline]
pub fn downcast<'b, T: NdpOption<'b>>(&'b mut self) -> Result<Immutable<'b, T>> {
T::try_parse(self.mbuf, self.offset, Internal(())).map(Immutable::new)
}
}
impl fmt::Debug for ImmutableNdpOption<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImmutableNdpOption")
.field("option_type", &self.option_type())
.field("length", &self.length())
.field("$offset", &self.offset)
.field("$len", &(self.length() * 8))
.finish()
}
}
pub struct ImmutableNdpOptionsIterator<'a> {
mbuf: Mbuf,
offset: usize,
_phantom: PhantomData<&'a Mbuf>,
}
impl ImmutableNdpOptionsIterator<'_> {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<ImmutableNdpOption<'_>>> {
if self.mbuf.data_len() > self.offset {
match ImmutableNdpOption::new(&mut self.mbuf, self.offset) {
Ok(option) => {
self.offset = option.end_offset();
Ok(Some(option))
}
Err(e) => Err(e),
}
} else {
Ok(None)
}
}
}
impl fmt::Debug for ImmutableNdpOptionsIterator<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImmutableNdpOptionsIterator")
.field("offset", &self.offset)
.finish()
}
}
pub struct MutableNdpOption<'a> {
mbuf: &'a mut Mbuf,
tuple: NonNull<TypeLengthTuple>,
offset: usize,
}
impl<'a> MutableNdpOption<'a> {
#[inline]
fn new(mbuf: &'a mut Mbuf, offset: usize) -> Result<Self> {
let tuple = mbuf.read_data(offset)?;
let option = MutableNdpOption {
mbuf,
tuple,
offset,
};
ensure!(
option.mbuf.len() >= option.end_offset(),
BufferError::OutOfBuffer(option.end_offset(), option.mbuf.len())
);
Ok(option)
}
#[inline]
fn tuple(&self) -> &TypeLengthTuple {
unsafe { self.tuple.as_ref() }
}
#[inline]
pub fn option_type(&self) -> NdpOptionType {
NdpOptionType(self.tuple().option_type)
}
#[inline]
pub fn length(&self) -> u8 {
self.tuple().length
}
#[inline]
fn end_offset(&self) -> usize {
self.offset + self.length() as usize * 8
}
#[inline]
pub fn downcast<'b, T: NdpOption<'b>>(&'b mut self) -> Result<T> {
T::try_parse(self.mbuf, self.offset, Internal(()))
}
}
impl fmt::Debug for MutableNdpOption<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MutableNdpOption")
.field("option_type", &self.option_type())
.field("length", &self.length())
.field("$offset", &self.offset)
.field("$len", &(self.length() * 8))
.finish()
}
}
pub struct MutableNdpOptionsIterator<'a> {
mbuf: &'a mut Mbuf,
offset: usize,
}
impl MutableNdpOptionsIterator<'_> {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<MutableNdpOption<'_>>> {
if self.mbuf.data_len() > self.offset {
match MutableNdpOption::new(&mut self.mbuf, self.offset) {
Ok(option) => {
self.offset = option.end_offset();
Ok(Some(option))
}
Err(e) => Err(e),
}
} else {
Ok(None)
}
}
}
impl fmt::Debug for MutableNdpOptionsIterator<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MutableNdpOptionsIterator")
.field("offset", &self.offset)
.finish()
}
}
pub struct NdpOptions<'a> {
mbuf: &'a mut Mbuf,
offset: usize,
}
impl NdpOptions<'_> {
#[inline]
pub fn iter(&mut self) -> MutableNdpOptionsIterator<'_> {
MutableNdpOptionsIterator {
mbuf: self.mbuf,
offset: self.offset,
}
}
#[inline]
pub fn prepend<'a, T: NdpOption<'a>>(&'a mut self) -> Result<T> {
T::try_push(self.mbuf, self.offset, Internal(()))
}
#[inline]
pub fn append<'a, T: NdpOption<'a>>(&'a mut self) -> Result<T> {
T::try_push(self.mbuf, self.mbuf.data_len(), Internal(()))
}
pub fn retain<F>(&mut self, mut f: F) -> Result<()>
where
F: FnMut(&mut ImmutableNdpOption<'_>) -> bool,
{
let mut offset = self.offset;
while self.mbuf.data_len() > offset {
let mut option = ImmutableNdpOption::new(self.mbuf, offset)?;
if !f(&mut option) {
let len = option.length() * 8;
self.mbuf.shrink(offset, len as usize)?;
} else {
offset = option.end_offset();
}
}
Ok(())
}
}
impl fmt::Debug for NdpOptions<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NdpOptions")
.field("offset", &self.offset)
.finish()
}
}
pub trait NdpOption<'a> {
fn option_type(&self) -> NdpOptionType;
fn length(&self) -> u8;
fn try_parse(mbuf: &'a mut Mbuf, offset: usize, internal: Internal) -> Result<Self>
where
Self: Sized;
fn try_push(mbuf: &'a mut Mbuf, offset: usize, internal: Internal) -> Result<Self>
where
Self: Sized;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packets::ip::v6::Ipv6;
use crate::packets::Ethernet;
use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET;
#[capsule::test]
fn iterate_immutable_ndp_options() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut prefix = false;
let mut mtu = false;
let mut source = false;
let mut other = false;
let mut iter = advert.options_iter();
while let Some(option) = iter.next().unwrap() {
match option.option_type() {
NdpOptionTypes::PrefixInformation => prefix = true,
NdpOptionTypes::Mtu => mtu = true,
NdpOptionTypes::SourceLinkLayerAddress => source = true,
_ => other = true,
}
}
assert!(prefix);
assert!(mtu);
assert!(source);
assert!(other);
}
#[capsule::test]
fn invalid_ndp_option_length() {
let packet = Mbuf::from_bytes(&INVALID_OPTION_LENGTH).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
assert!(advert.options_iter().next().is_err());
}
#[capsule::test]
fn downcast_immutable_ndp_option() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.peek::<Ethernet>().unwrap();
let ipv6 = ethernet.peek::<Ipv6>().unwrap();
let advert = ipv6.peek::<RouterAdvertisement<Ipv6>>().unwrap();
let mut iter = advert.options_iter();
let mut prefix = iter.next().unwrap().unwrap();
assert!(prefix.downcast::<PrefixInformation<'_>>().is_ok());
let mut mtu = iter.next().unwrap().unwrap();
assert!(mtu.downcast::<PrefixInformation<'_>>().is_err());
}
#[capsule::test]
fn iterate_mutable_ndp_options() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut prefix = false;
let mut mtu = false;
let mut source = false;
let mut unknown = false;
let mut options = advert.options_mut();
let mut iter = options.iter();
while let Some(option) = iter.next().unwrap() {
match option.option_type() {
NdpOptionTypes::PrefixInformation => prefix = true,
NdpOptionTypes::Mtu => mtu = true,
NdpOptionTypes::SourceLinkLayerAddress => source = true,
_ => unknown = true, }
}
assert!(prefix);
assert!(mtu);
assert!(source);
assert!(unknown);
}
#[capsule::test]
fn downcast_mutable_ndp_option() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let mut iter = options.iter();
let mut prefix = iter.next().unwrap().unwrap();
assert!(prefix.downcast::<PrefixInformation<'_>>().is_ok());
let mut mtu = iter.next().unwrap().unwrap();
assert!(mtu.downcast::<PrefixInformation<'_>>().is_err());
}
#[capsule::test]
fn modify_ndp_option() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let mut iter = options.iter();
let mut option = iter.next().unwrap().unwrap();
let mut prefix = option.downcast::<PrefixInformation<'_>>().unwrap();
assert_eq!(64, prefix.prefix_length());
prefix.set_prefix_length(32);
assert_eq!(32, prefix.prefix_length());
}
#[capsule::test]
fn prepend_ndp_option() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let mut target = options.prepend::<LinkLayerAddress<'_>>().unwrap();
target.set_option_type_target();
let mut iter = advert.options_iter();
let first = iter.next().unwrap().unwrap();
assert_eq!(NdpOptionTypes::TargetLinkLayerAddress, first.option_type());
}
#[capsule::test]
fn append_ndp_option() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let mut target = options.append::<LinkLayerAddress<'_>>().unwrap();
target.set_option_type_target();
let mut index = 0;
let mut iter = advert.options_iter();
while let Some(option) = iter.next().unwrap() {
if option.option_type() == NdpOptionTypes::TargetLinkLayerAddress {
break;
}
index += 1;
}
assert_eq!(4, index);
}
#[capsule::test]
fn retain_ndp_options() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let _ = options.retain(|option| option.downcast::<Mtu<'_>>().is_ok());
let mut iter = advert.options_iter();
assert!(iter.next().unwrap().is_some());
assert!(iter.next().unwrap().is_none());
}
#[test]
#[cfg(feature = "compile_failure")]
fn cannot_mutate_packet_while_iterating_options() {
use crate::packets::icmp::v6::Icmpv6Packet;
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut iter = advert.options_iter();
advert.set_code(0);
let _ = iter.next();
}
#[test]
#[cfg(feature = "compile_failure")]
fn cannot_mutate_immutable_option() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut iter = advert.options_iter();
let mut option = iter.next().unwrap().unwrap();
let prefix = option.downcast::<PrefixInformation<'_>>().unwrap();
prefix.set_prefix_length(64);
}
#[test]
#[cfg(feature = "compile_failure")]
fn cannot_mutate_options_while_iterating_options() {
let packet = Mbuf::from_bytes(&ROUTER_ADVERT_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv6 = ethernet.parse::<Ipv6>().unwrap();
let mut advert = ipv6.parse::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let mut iter = options.iter();
let _ = options.prepend::<LinkLayerAddress<'_>>();
let _ = iter.next();
}
#[rustfmt::skip]
const INVALID_OPTION_LENGTH: [u8;78] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x86, 0xDD,
0x60, 0x00, 0x00, 0x00,
0x00, 0x18,
0x3a,
0xff,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xf0, 0x45, 0xff, 0xfe, 0x0c, 0x66, 0x4b,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x86,
0x00,
0xf5, 0x0c,
0x40,
0x58,
0x07, 0x08,
0x00,0x00, 0x08, 0x07,
0x00,0x00, 0x05, 0xdc,
0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc,
];
}