use crate::packets::icmp::v6::{Icmpv6, Icmpv6Message, Icmpv6Packet, Icmpv6Type, Icmpv6Types};
use crate::packets::ip::v6::Ipv6Packet;
use crate::packets::types::u16be;
use crate::packets::{Internal, Packet};
use crate::SizeOf;
use anyhow::Result;
use std::fmt;
use std::ptr::NonNull;
#[derive(Icmpv6Packet)]
pub struct EchoRequest<E: Ipv6Packet> {
icmp: Icmpv6<E>,
body: NonNull<EchoRequestBody>,
}
impl<E: Ipv6Packet> EchoRequest<E> {
#[inline]
fn body(&self) -> &EchoRequestBody {
unsafe { self.body.as_ref() }
}
#[inline]
fn body_mut(&mut self) -> &mut EchoRequestBody {
unsafe { self.body.as_mut() }
}
#[inline]
pub fn identifier(&self) -> u16 {
self.body().identifier.into()
}
#[inline]
pub fn set_identifier(&mut self, identifier: u16) {
self.body_mut().identifier = identifier.into();
}
#[inline]
pub fn seq_no(&self) -> u16 {
self.body().seq_no.into()
}
#[inline]
pub fn set_seq_no(&mut self, seq_no: u16) {
self.body_mut().seq_no = seq_no.into();
}
#[inline]
fn data_offset(&self) -> usize {
self.payload_offset() + EchoRequestBody::size_of()
}
#[inline]
fn data_len(&self) -> usize {
self.payload_len() - EchoRequestBody::size_of()
}
#[inline]
pub fn data(&self) -> &[u8] {
if let Ok(data) = self
.icmp()
.mbuf()
.read_data_slice(self.data_offset(), self.data_len())
{
unsafe { &*data.as_ptr() }
} else {
unreachable!()
}
}
#[inline]
pub fn set_data(&mut self, data: &[u8]) -> Result<()> {
let offset = self.data_offset();
let len = data.len() as isize - self.data_len() as isize;
self.icmp_mut().mbuf_mut().resize(offset, len)?;
self.icmp_mut().mbuf_mut().write_data_slice(offset, data)?;
Ok(())
}
}
impl<E: Ipv6Packet> fmt::Debug for EchoRequest<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EchoRequest")
.field("type", &format!("{}", self.msg_type()))
.field("code", &self.code())
.field("checksum", &format!("0x{:04x}", self.checksum()))
.field("identifier", &self.identifier())
.field("seq_no", &self.seq_no())
.field("$offset", &self.offset())
.field("$len", &self.len())
.field("$header_len", &self.header_len())
.finish()
}
}
impl<E: Ipv6Packet> Icmpv6Message for EchoRequest<E> {
type Envelope = E;
#[inline]
fn msg_type() -> Icmpv6Type {
Icmpv6Types::EchoRequest
}
#[inline]
fn icmp(&self) -> &Icmpv6<Self::Envelope> {
&self.icmp
}
#[inline]
fn icmp_mut(&mut self) -> &mut Icmpv6<Self::Envelope> {
&mut self.icmp
}
#[inline]
fn into_icmp(self) -> Icmpv6<Self::Envelope> {
self.icmp
}
#[inline]
unsafe fn clone(&self, internal: Internal) -> Self {
EchoRequest {
icmp: self.icmp.clone(internal),
body: self.body,
}
}
#[inline]
fn try_parse(icmp: Icmpv6<Self::Envelope>, _internal: Internal) -> Result<Self> {
let mbuf = icmp.mbuf();
let offset = icmp.payload_offset();
let body = mbuf.read_data(offset)?;
Ok(EchoRequest { icmp, body })
}
#[inline]
fn try_push(mut icmp: Icmpv6<Self::Envelope>, _internal: Internal) -> Result<Self> {
let offset = icmp.payload_offset();
let mbuf = icmp.mbuf_mut();
mbuf.extend(offset, EchoRequestBody::size_of())?;
let body = mbuf.write_data(offset, &EchoRequestBody::default())?;
Ok(EchoRequest { icmp, body })
}
}
#[derive(Clone, Copy, Debug, Default, SizeOf)]
#[repr(C, packed)]
struct EchoRequestBody {
identifier: u16be,
seq_no: u16be,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packets::ip::v6::Ipv6;
use crate::packets::Ethernet;
use crate::Mbuf;
#[test]
fn size_of_echo_request_body() {
assert_eq!(4, EchoRequestBody::size_of());
}
#[capsule::test]
fn push_and_set_echo_request() {
let packet = Mbuf::new().unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
let ipv6 = ethernet.push::<Ipv6>().unwrap();
let mut echo = ipv6.push::<EchoRequest<Ipv6>>().unwrap();
assert_eq!(4, echo.header_len());
assert_eq!(EchoRequestBody::size_of(), echo.payload_len());
assert_eq!(Icmpv6Types::EchoRequest, echo.msg_type());
assert_eq!(0, echo.code());
echo.set_identifier(42);
assert_eq!(42, echo.identifier());
echo.set_seq_no(7);
assert_eq!(7, echo.seq_no());
let data = [0; 10];
assert!(echo.set_data(&data).is_ok());
assert_eq!(&data, echo.data());
assert_eq!(EchoRequestBody::size_of() + 10, echo.payload_len());
echo.reconcile_all();
assert!(echo.checksum() != 0);
}
}