use crate::packets::icmp::v6::ndp::{NdpOption, NdpOptionType, NdpOptionTypes};
use crate::packets::types::u32be;
use crate::packets::Internal;
use crate::{ensure, Mbuf, SizeOf};
use anyhow::{anyhow, Result};
use std::fmt;
use std::net::Ipv6Addr;
use std::ptr::NonNull;
const ONLINK: u8 = 0b1000_0000;
const AUTO: u8 = 0b0100_0000;
pub struct PrefixInformation<'a> {
_mbuf: &'a mut Mbuf,
fields: NonNull<PrefixInformationFields>,
offset: usize,
}
impl PrefixInformation<'_> {
#[inline]
fn fields(&self) -> &PrefixInformationFields {
unsafe { self.fields.as_ref() }
}
#[inline]
fn fields_mut(&mut self) -> &mut PrefixInformationFields {
unsafe { self.fields.as_mut() }
}
#[inline]
pub fn prefix_length(&self) -> u8 {
self.fields().prefix_length
}
#[inline]
pub fn set_prefix_length(&mut self, prefix_length: u8) {
self.fields_mut().prefix_length = prefix_length
}
#[inline]
pub fn on_link(&self) -> bool {
self.fields().flags & ONLINK > 0
}
#[inline]
pub fn set_on_link(&mut self) {
self.fields_mut().flags |= ONLINK;
}
#[inline]
pub fn unset_on_link(&mut self) {
self.fields_mut().flags &= !ONLINK;
}
#[inline]
pub fn autonomous(&self) -> bool {
self.fields().flags & AUTO > 0
}
#[inline]
pub fn set_autonomous(&mut self) {
self.fields_mut().flags |= AUTO;
}
#[inline]
pub fn unset_autonomous(&mut self) {
self.fields_mut().flags &= !AUTO;
}
#[inline]
pub fn valid_lifetime(&self) -> u32 {
self.fields().valid_lifetime.into()
}
#[inline]
pub fn set_valid_lifetime(&mut self, valid_lifetime: u32) {
self.fields_mut().valid_lifetime = valid_lifetime.into();
}
#[inline]
pub fn preferred_lifetime(&self) -> u32 {
self.fields().preferred_lifetime.into()
}
#[inline]
pub fn set_preferred_lifetime(&mut self, preferred_lifetime: u32) {
self.fields_mut().preferred_lifetime = preferred_lifetime.into();
}
#[inline]
pub fn prefix(&self) -> Ipv6Addr {
self.fields().prefix
}
#[inline]
pub fn set_prefix(&mut self, prefix: Ipv6Addr) {
self.fields_mut().prefix = prefix;
}
}
impl fmt::Debug for PrefixInformation<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PrefixInformation")
.field("type", &self.option_type())
.field("length", &self.length())
.field("prefix_length", &self.prefix_length())
.field("on_link", &self.on_link())
.field("autonomous", &self.autonomous())
.field("valid_lifetime", &self.valid_lifetime())
.field("preferred_lifetime", &self.preferred_lifetime())
.field("prefix", &self.prefix())
.field("$offset", &self.offset)
.finish()
}
}
impl<'a> NdpOption<'a> for PrefixInformation<'a> {
#[inline]
fn option_type(&self) -> NdpOptionType {
NdpOptionType(self.fields().option_type)
}
#[inline]
fn length(&self) -> u8 {
self.fields().length
}
#[inline]
fn try_parse(
mbuf: &'a mut Mbuf,
offset: usize,
_internal: Internal,
) -> Result<PrefixInformation<'a>> {
let fields = mbuf.read_data::<PrefixInformationFields>(offset)?;
let option = PrefixInformation {
_mbuf: mbuf,
fields,
offset,
};
ensure!(
option.option_type() == NdpOptionTypes::PrefixInformation,
anyhow!("not prefix information.")
);
ensure!(
option.length() * 8 == PrefixInformationFields::size_of() as u8,
anyhow!("invalid prefix information option length.")
);
Ok(option)
}
#[inline]
fn try_push(
mbuf: &'a mut Mbuf,
offset: usize,
_internal: Internal,
) -> Result<PrefixInformation<'a>> {
mbuf.extend(offset, PrefixInformationFields::size_of())?;
let fields = mbuf.write_data(offset, &PrefixInformationFields::default())?;
Ok(PrefixInformation {
_mbuf: mbuf,
fields,
offset,
})
}
}
#[derive(Clone, Copy, Debug, SizeOf)]
#[repr(C)]
struct PrefixInformationFields {
option_type: u8,
length: u8,
prefix_length: u8,
flags: u8,
valid_lifetime: u32be,
preferred_lifetime: u32be,
reserved: u32be,
prefix: Ipv6Addr,
}
impl Default for PrefixInformationFields {
fn default() -> PrefixInformationFields {
PrefixInformationFields {
option_type: NdpOptionTypes::PrefixInformation.0,
length: 4,
prefix_length: 0,
flags: 0,
valid_lifetime: u32be::default(),
preferred_lifetime: u32be::default(),
reserved: u32be::default(),
prefix: Ipv6Addr::UNSPECIFIED,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packets::icmp::v6::ndp::{NdpPacket, RouterAdvertisement};
use crate::packets::ip::v6::Ipv6;
use crate::packets::{Ethernet, Packet};
use crate::testils::byte_arrays::ROUTER_ADVERT_PACKET;
#[test]
fn size_of_prefix_information_fields() {
assert_eq!(32, PrefixInformationFields::size_of());
}
#[capsule::test]
fn parse_prefix_information() {
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 pass = false;
while let Some(mut option) = iter.next().unwrap() {
if let Ok(prefix) = option.downcast::<PrefixInformation<'_>>() {
assert_eq!(NdpOptionTypes::PrefixInformation, prefix.option_type());
assert_eq!(4, prefix.length());
assert_eq!(64, prefix.prefix_length());
assert!(prefix.on_link());
assert!(prefix.autonomous());
assert_eq!(2366, prefix.valid_lifetime());
assert_eq!(2366, prefix.preferred_lifetime());
assert_eq!(
Ipv6Addr::new(0x2607, 0xfcc8, 0xf142, 0xb0f0, 0, 0, 0, 0),
prefix.prefix()
);
pass = true;
break;
}
}
assert!(pass);
}
#[capsule::test]
fn push_and_set_prefix_information() {
let packet = Mbuf::new().unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
let ipv6 = ethernet.push::<Ipv6>().unwrap();
let mut advert = ipv6.push::<RouterAdvertisement<Ipv6>>().unwrap();
let mut options = advert.options_mut();
let mut prefix = options.append::<PrefixInformation<'_>>().unwrap();
assert_eq!(NdpOptionTypes::PrefixInformation, prefix.option_type());
assert_eq!(4, prefix.length());
assert_eq!(0, prefix.prefix_length());
assert!(!prefix.on_link());
assert!(!prefix.autonomous());
assert_eq!(0, prefix.valid_lifetime());
assert_eq!(0, prefix.preferred_lifetime());
assert_eq!(Ipv6Addr::UNSPECIFIED, prefix.prefix());
prefix.set_prefix_length(64);
assert_eq!(64, prefix.prefix_length());
prefix.set_on_link();
assert!(prefix.on_link());
prefix.unset_on_link();
assert!(!prefix.on_link());
prefix.set_autonomous();
assert!(prefix.autonomous());
prefix.unset_autonomous();
assert!(!prefix.autonomous());
prefix.set_valid_lifetime(255);
assert_eq!(255, prefix.valid_lifetime());
prefix.set_preferred_lifetime(600);
assert_eq!(600, prefix.preferred_lifetime());
prefix.set_prefix(Ipv6Addr::LOCALHOST);
assert_eq!(Ipv6Addr::LOCALHOST, prefix.prefix());
}
}