use crate::net::MacAddr;
use crate::packets::icmp::v6::ndp::{NdpOption, NdpOptionType, NdpOptionTypes};
use crate::packets::Internal;
use crate::{ensure, Mbuf, SizeOf};
use anyhow::{anyhow, Result};
use std::fmt;
use std::ptr::NonNull;
pub struct LinkLayerAddress<'a> {
_mbuf: &'a mut Mbuf,
fields: NonNull<LinkLayerAddressFields>,
offset: usize,
}
impl LinkLayerAddress<'_> {
#[inline]
fn fields(&self) -> &LinkLayerAddressFields {
unsafe { self.fields.as_ref() }
}
#[inline]
fn fields_mut(&mut self) -> &mut LinkLayerAddressFields {
unsafe { self.fields.as_mut() }
}
#[inline]
pub fn set_option_type_source(&mut self) {
self.fields_mut().option_type = NdpOptionTypes::SourceLinkLayerAddress.0;
}
#[inline]
pub fn set_option_type_target(&mut self) {
self.fields_mut().option_type = NdpOptionTypes::TargetLinkLayerAddress.0;
}
#[inline]
pub fn addr(&self) -> MacAddr {
self.fields().addr
}
#[inline]
pub fn set_addr(&mut self, addr: MacAddr) {
self.fields_mut().addr = addr;
}
}
impl fmt::Debug for LinkLayerAddress<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LinkLayerAddress")
.field("type", &self.option_type())
.field("length", &self.length())
.field("addr", &self.addr())
.field("$offset", &self.offset)
.finish()
}
}
impl<'a> NdpOption<'a> for LinkLayerAddress<'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<LinkLayerAddress<'a>> {
let fields = mbuf.read_data::<LinkLayerAddressFields>(offset)?;
let option = LinkLayerAddress {
_mbuf: mbuf,
fields,
offset,
};
ensure!(
option.option_type() == NdpOptionTypes::SourceLinkLayerAddress
|| option.option_type() == NdpOptionTypes::TargetLinkLayerAddress,
anyhow!("not source/target link-layer address.")
);
ensure!(
option.length() * 8 == LinkLayerAddressFields::size_of() as u8,
anyhow!("invalid link-layer address option length.")
);
Ok(option)
}
#[inline]
fn try_push(
mbuf: &'a mut Mbuf,
offset: usize,
_internal: Internal,
) -> Result<LinkLayerAddress<'a>> {
mbuf.extend(offset, LinkLayerAddressFields::size_of())?;
let fields = mbuf.write_data(offset, &LinkLayerAddressFields::default())?;
Ok(LinkLayerAddress {
_mbuf: mbuf,
fields,
offset,
})
}
}
#[derive(Clone, Copy, Debug, SizeOf)]
#[repr(C, packed)]
struct LinkLayerAddressFields {
option_type: u8,
length: u8,
addr: MacAddr,
}
impl Default for LinkLayerAddressFields {
fn default() -> LinkLayerAddressFields {
LinkLayerAddressFields {
option_type: NdpOptionTypes::SourceLinkLayerAddress.0,
length: 1,
addr: MacAddr::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_link_layer_address_fields() {
assert_eq!(8, LinkLayerAddressFields::size_of());
}
#[capsule::test]
fn parse_link_layer_address() {
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(lla) = option.downcast::<LinkLayerAddress<'_>>() {
assert_eq!(NdpOptionTypes::SourceLinkLayerAddress, lla.option_type());
assert_eq!(1, lla.length());
assert_eq!(MacAddr::new(0x70, 0x3a, 0xcb, 0x1b, 0xf9, 0x7a), lla.addr());
pass = true;
break;
}
}
assert!(pass);
}
#[capsule::test]
fn push_and_set_link_layer_address() {
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 lla = options.append::<LinkLayerAddress<'_>>().unwrap();
assert_eq!(NdpOptionTypes::SourceLinkLayerAddress, lla.option_type());
assert_eq!(1, lla.length());
assert_eq!(MacAddr::UNSPECIFIED, lla.addr());
lla.set_option_type_target();
assert_eq!(NdpOptionTypes::TargetLinkLayerAddress, lla.option_type());
lla.set_option_type_source();
assert_eq!(NdpOptionTypes::SourceLinkLayerAddress, lla.option_type());
lla.set_addr(MacAddr::new(1, 1, 1, 1, 1, 1));
assert_eq!(MacAddr::new(1, 1, 1, 1, 1, 1), lla.addr());
}
}