use crate::{
error::{Error, Result},
protocols::ip::translate_ipv4_to_ipv6,
};
use pnet::packet::{
icmp::{self, IcmpPacket, IcmpTypes, MutableIcmpPacket},
icmpv6::{self, Icmpv6Packet, Icmpv6Types, MutableIcmpv6Packet},
Packet,
};
use std::net::{Ipv4Addr, Ipv6Addr};
use super::ip::translate_ipv6_to_ipv4;
mod type_code;
#[allow(clippy::deprecated_cfg_attr)]
#[profiling::function]
pub fn translate_icmp_to_icmpv6(
icmp_packet: &[u8],
new_source: Ipv6Addr,
new_destination: Ipv6Addr,
) -> Result<Vec<u8>> {
{
let icmp_packet = IcmpPacket::new(icmp_packet).ok_or(Error::PacketTooShort {
expected: IcmpPacket::minimum_packet_size(),
actual: icmp_packet.len(),
})?;
#[cfg(feature = "metrics")]
protomask_metrics::metrics::ICMP_COUNTER
.with_label_values(&[
protomask_metrics::metrics::label_values::PROTOCOL_ICMP,
&icmp_packet.get_icmp_type().0.to_string(),
&icmp_packet.get_icmp_code().0.to_string(),
])
.inc();
let (icmpv6_type, icmpv6_code) = type_code::translate_type_and_code_4_to_6(
icmp_packet.get_icmp_type(),
icmp_packet.get_icmp_code(),
)?;
let payload = match icmpv6_type {
Icmpv6Types::TimeExceeded => {
let mut output = vec![0u8; 4];
output.copy_from_slice(&icmp_packet.payload()[..4]);
output.extend_from_slice(&translate_ipv4_to_ipv6(
&icmp_packet.payload()[4..],
new_source,
new_destination,
)?);
output
}
_ => icmp_packet.payload().to_vec(),
};
let mut output_buffer = vec![0u8; IcmpPacket::minimum_packet_size() + payload.len()];
let mut icmpv6_packet =
unsafe { MutableIcmpv6Packet::new(&mut output_buffer).unwrap_unchecked() };
icmpv6_packet.set_icmpv6_type(icmpv6_type);
icmpv6_packet.set_icmpv6_code(icmpv6_code);
icmpv6_packet.set_checksum(0);
icmpv6_packet.set_payload(&payload);
icmpv6_packet.set_checksum(icmpv6::checksum(
&icmpv6_packet.to_immutable(),
&new_source,
&new_destination,
));
#[cfg(feature = "metrics")]
protomask_metrics::metric!(PACKET_COUNTER, PROTOCOL_ICMP, STATUS_TRANSLATED).inc();
Ok(output_buffer)
}
.map_err(|error| {
#[cfg(feature = "metrics")]
protomask_metrics::metric!(PACKET_COUNTER, PROTOCOL_ICMP, STATUS_DROPPED).inc();
error
})
}
#[profiling::function]
pub fn translate_icmpv6_to_icmp(
icmpv6_packet: &[u8],
new_source: Ipv4Addr,
new_destination: Ipv4Addr,
) -> Result<Vec<u8>> {
{
let icmpv6_packet = Icmpv6Packet::new(icmpv6_packet).ok_or(Error::PacketTooShort {
expected: Icmpv6Packet::minimum_packet_size(),
actual: icmpv6_packet.len(),
})?;
#[cfg(feature = "metrics")]
protomask_metrics::metrics::ICMP_COUNTER
.with_label_values(&[
protomask_metrics::metrics::label_values::PROTOCOL_ICMPV6,
&icmpv6_packet.get_icmpv6_type().0.to_string(),
&icmpv6_packet.get_icmpv6_code().0.to_string(),
])
.inc();
let (icmp_type, icmp_code) = type_code::translate_type_and_code_6_to_4(
icmpv6_packet.get_icmpv6_type(),
icmpv6_packet.get_icmpv6_code(),
)?;
let payload = match icmp_type {
IcmpTypes::TimeExceeded => {
let mut output = vec![0u8; 4];
output.copy_from_slice(&icmpv6_packet.payload()[..4]);
output.extend_from_slice(&translate_ipv6_to_ipv4(
&icmpv6_packet.payload()[4..],
new_source,
new_destination,
)?);
output
}
_ => icmpv6_packet.payload().to_vec(),
};
let mut output_buffer = vec![0u8; Icmpv6Packet::minimum_packet_size() + payload.len()];
let mut icmp_packet =
unsafe { MutableIcmpPacket::new(&mut output_buffer).unwrap_unchecked() };
icmp_packet.set_icmp_type(icmp_type);
icmp_packet.set_icmp_code(icmp_code);
icmp_packet.set_payload(&payload);
icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable()));
Ok(output_buffer)
}
.map_err(|error| {
#[cfg(feature = "metrics")]
protomask_metrics::metric!(PACKET_COUNTER, PROTOCOL_ICMPV6, STATUS_DROPPED).inc();
error
})
}