use crate::nic::Info;
use crate::layer::{Error, Result, ip};
use crate::wire::{icmpv4, Checksum, Payload, PayloadMut};
use crate::wire::ip::{Address as IpAddress, Protocol as IpProtocol};
pub struct In<'a, P: Payload> {
pub control: Controller<'a>,
pub packet: icmpv4::Packet<ip::V4Packet<'a, P>>,
}
#[must_use = "You need to call `send` explicitely on an OutPacket, otherwise no packet is sent."]
pub struct Out<'a, P: Payload> {
control: Controller<'a>,
packet: icmpv4::Packet<ip::V4Packet<'a, P>>,
}
pub struct Raw<'a, P: Payload> {
pub control: Controller<'a>,
pub payload: &'a mut P,
}
pub struct Controller<'a> {
pub(crate) inner: ip::Controller<'a>,
}
pub enum Init {
EchoRequest {
source: ip::Source,
dst_addr: IpAddress,
ident: u16,
seq_no: u16,
payload: usize,
},
}
impl<'a> Controller<'a> {
pub fn info(&self) -> &dyn Info {
self.inner.info()
}
pub fn borrow_mut(&mut self) -> Controller {
Controller {
inner: self.inner.borrow_mut(),
}
}
}
impl<'a, P: Payload> In<'a, P> {
pub fn deinit(self) -> Raw<'a, P>
where P: PayloadMut,
{
Raw {
control: self.control,
payload: self.packet.into_inner().into_inner().into_inner(),
}
}
}
impl<'a, P: PayloadMut> In<'a, P> {
pub fn answer(self) -> Result<Out<'a, P>> {
let answer = match self.packet.repr() {
icmpv4::Repr::EchoRequest { ident, seq_no, payload } => {
icmpv4::Repr::EchoReply { ident, seq_no, payload }
},
_ => return Err(Error::Illegal),
};
let ipv4_packet = self.packet.into_inner();
let ip_repr = ipv4_packet.repr();
let ip_in = ip::InPacket {
control: self.control.inner,
packet: ip::IpPacket::V4(ipv4_packet),
};
let ip_out = ip_in.reinit(ip::Init {
source: IpAddress::from(ip_repr.dst_addr).into(),
dst_addr: ip_repr.src_addr.into(),
protocol: IpProtocol::Icmp,
payload: ip_repr.payload_len,
})?;
let ip::InPacket { control, mut packet } = ip_out.into_incoming();
answer.emit(
icmpv4::packet::new_unchecked_mut(packet.payload_mut().as_mut_slice()),
Checksum::Manual);
let packet = match packet {
ip::IpPacket::V4(packet) => packet,
ip::IpPacket::V6(_) => unreachable!("No icmpv6 outgoing traffic"),
};
Ok(Out {
control: Controller { inner: control },
packet: icmpv4::Packet::new_unchecked(packet, answer),
})
}
}
impl<'a, P: Payload> Out<'a, P> {
pub fn send(mut self) -> Result<()>
where P: PayloadMut,
{
let capabilities = self.control.info().capabilities();
let checksum = capabilities.icmpv4().tx_checksum();
self.packet.fill_checksum(checksum);
let lower = ip::OutPacket::new_unchecked(
self.control.inner,
ip::IpPacket::V4(self.packet.into_inner()));
lower.send()
}
}
impl<'a, P: PayloadMut> Out<'a, P> {
pub fn payload_mut_slice(&mut self) -> &mut [u8] {
self.packet.payload_mut_slice()
}
}
impl<'a, P: Payload + PayloadMut> Raw<'a, P> {
pub fn prepare(self, init: Init) -> Result<Out<'a, P>> {
let lower = ip::RawPacket {
control: self.control.inner,
payload: self.payload,
};
let lower_init = init.ip_init()?;
let prepared = lower.prepare(lower_init)?;
let ip::InPacket { control, packet } = prepared.into_incoming();
let mut packet = match packet {
ip::IpPacket::V4(packet) => packet,
ip::IpPacket::V6(_) => unreachable!(),
};
let repr = init.initialize(&mut packet)?;
Ok(Out {
control: Controller { inner: control },
packet: icmpv4::Packet::new_unchecked(packet, repr),
})
}
}
impl Init {
fn initialize(&self, payload: &mut impl PayloadMut) -> Result<icmpv4::Repr> {
let repr = self.repr();
let packet = icmpv4::packet::new_unchecked_mut(
payload.payload_mut().as_mut_slice());
repr.emit(packet, Checksum::Ignored);
Ok(repr)
}
fn repr(&self) -> icmpv4::Repr {
match *self {
Init::EchoRequest { ident, seq_no, payload, .. } => {
icmpv4::Repr::EchoRequest { ident, seq_no, payload }
},
}
}
fn ip_init(&self) -> Result<ip::Init> {
Ok(match *self {
Init::EchoRequest { source, payload, dst_addr, .. } => {
let len = payload
.checked_add(8)
.ok_or(Error::BadSize)?;
ip::Init {
source,
dst_addr,
protocol: IpProtocol::Icmp,
payload: len,
}
},
})
}
}