Cleaned up ICMP
This commit is contained in:
parent
c91f5d3b37
commit
039a82fd51
@ -1,3 +1,23 @@
|
|||||||
|
/// Quickly convert a byte slice into an ICMP packet
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! into_icmp {
|
||||||
|
($bytes:expr) => {
|
||||||
|
pnet_packet::icmp::IcmpPacket::owned($bytes).ok_or_else(|| {
|
||||||
|
crate::nat::xlat::PacketTranslationError::InputPacketTooShort($bytes.len())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quickly convert a byte slice into an ICMPv6 packet
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! into_icmpv6 {
|
||||||
|
($bytes:expr) => {
|
||||||
|
pnet_packet::icmpv6::Icmpv6Packet::owned($bytes).ok_or_else(|| {
|
||||||
|
crate::nat::xlat::PacketTranslationError::InputPacketTooShort($bytes.len())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Quickly convert a byte slice into a UDP packet
|
/// Quickly convert a byte slice into a UDP packet
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! into_udp {
|
macro_rules! into_udp {
|
||||||
|
@ -6,7 +6,9 @@ use std::{
|
|||||||
use ipnet::{Ipv4Net, Ipv6Net};
|
use ipnet::{Ipv4Net, Ipv6Net};
|
||||||
use pnet_packet::{ip::IpNextHeaderProtocols, Packet};
|
use pnet_packet::{ip::IpNextHeaderProtocols, Packet};
|
||||||
|
|
||||||
use crate::{into_tcp, into_udp, ipv4_packet, ipv6_packet, nat::xlat::translate_udp_4_to_6};
|
use crate::{
|
||||||
|
into_icmp, into_tcp, into_udp, ipv4_packet, ipv6_packet, nat::xlat::translate_udp_4_to_6, into_icmpv6,
|
||||||
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
interface::Nat64Interface,
|
interface::Nat64Interface,
|
||||||
@ -157,6 +159,22 @@ impl Nat64 {
|
|||||||
match (packet, new_source, new_destination) {
|
match (packet, new_source, new_destination) {
|
||||||
(IpPacket::V4(packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => {
|
(IpPacket::V4(packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => {
|
||||||
match packet.get_next_level_protocol() {
|
match packet.get_next_level_protocol() {
|
||||||
|
// Internet Control Message Protocol
|
||||||
|
IpNextHeaderProtocols::Icmp => match xlat::translate_icmp_4_to_6(
|
||||||
|
into_icmp!(packet.payload().to_vec())?,
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
)? {
|
||||||
|
Some(icmp_packet) => Ok(Some(IpPacket::V6(ipv6_packet!(
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
IpNextHeaderProtocols::Icmp,
|
||||||
|
packet.get_ttl(),
|
||||||
|
icmp_packet.packet()
|
||||||
|
)))),
|
||||||
|
None => Ok(None),
|
||||||
|
},
|
||||||
|
|
||||||
// User Datagram Protocol
|
// User Datagram Protocol
|
||||||
IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V6(ipv6_packet!(
|
IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V6(ipv6_packet!(
|
||||||
new_source,
|
new_source,
|
||||||
@ -194,6 +212,22 @@ impl Nat64 {
|
|||||||
}
|
}
|
||||||
(IpPacket::V6(packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => {
|
(IpPacket::V6(packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => {
|
||||||
match packet.get_next_header() {
|
match packet.get_next_header() {
|
||||||
|
// Internet Control Message Protocol Version 6
|
||||||
|
IpNextHeaderProtocols::Icmpv6 => match xlat::translate_icmp_6_to_4(
|
||||||
|
into_icmpv6!(packet.payload().to_vec())?,
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
)? {
|
||||||
|
Some(icmp_packet) => Ok(Some(IpPacket::V4(ipv4_packet!(
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
packet.get_hop_limit(),
|
||||||
|
IpNextHeaderProtocols::Icmp,
|
||||||
|
icmp_packet.packet()
|
||||||
|
)))),
|
||||||
|
None => Ok(None),
|
||||||
|
},
|
||||||
|
|
||||||
// User Datagram Protocol
|
// User Datagram Protocol
|
||||||
IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V4(ipv4_packet!(
|
IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V4(ipv4_packet!(
|
||||||
new_source,
|
new_source,
|
||||||
|
@ -1,329 +1,532 @@
|
|||||||
//! Translation logic for ICMP and ICMPv6
|
// //! Translation logic for ICMP and ICMPv6
|
||||||
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
|
// use pnet_packet::{
|
||||||
|
// icmp::{self, IcmpCode, IcmpPacket, IcmpType, MutableIcmpPacket},
|
||||||
|
// icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, MutableIcmpv6Packet},
|
||||||
|
// ip::IpNextHeaderProtocols,
|
||||||
|
// ipv4::{self, Ipv4Packet, MutableIpv4Packet},
|
||||||
|
// ipv6::{Ipv6Packet, MutableIpv6Packet},
|
||||||
|
// Packet,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// use crate::nat::packet::IpPacket;
|
||||||
|
|
||||||
|
// fn remap_values_4to6(
|
||||||
|
// icmp_type: IcmpType,
|
||||||
|
// icmp_code: IcmpCode,
|
||||||
|
// new_source: Ipv6Addr,
|
||||||
|
// new_destination: Ipv6Addr,
|
||||||
|
// payload: Vec<u8>,
|
||||||
|
// ) -> Option<(Icmpv6Type, Icmpv6Code, Vec<u8>)> {
|
||||||
|
// match icmp_type {
|
||||||
|
// // Destination Unreachable
|
||||||
|
// IcmpType(3) => match icmp_code {
|
||||||
|
// IcmpCode(0) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination network unreachable -> No route to destination
|
||||||
|
// IcmpCode(1) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Destination host unreachable -> Address unreachable
|
||||||
|
// IcmpCode(2) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination protocol unreachable -> No route to destination
|
||||||
|
// IcmpCode(3) => Some((Icmpv6Type(1), Icmpv6Code(4), payload)), // Destination port unreachable -> Port unreachable
|
||||||
|
// IcmpCode(4) => Some((Icmpv6Type(2), Icmpv6Code(0), vec![])), // Fragmentation required, and DF flag set -> Packet too big
|
||||||
|
// IcmpCode(5) => Some((Icmpv6Type(1), Icmpv6Code(5), payload)), // Source route failed -> Source address failed ingress/egress policy
|
||||||
|
// IcmpCode(6) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination network unknown -> No route to destination
|
||||||
|
// IcmpCode(7) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Destination host unknown -> Address unreachable
|
||||||
|
// IcmpCode(8) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Source host isolated -> No route to destination
|
||||||
|
// IcmpCode(9) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Network administratively prohibited -> Communication with destination administratively prohibited
|
||||||
|
// IcmpCode(10) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Host administratively prohibited -> Communication with destination administratively prohibited
|
||||||
|
// IcmpCode(11) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Network unreachable for ToS -> No route to destination
|
||||||
|
// IcmpCode(12) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Host unreachable for ToS -> Address unreachable
|
||||||
|
// IcmpCode(13) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Communication administratively prohibited -> Communication with destination administratively prohibited
|
||||||
|
// IcmpCode(14) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Host Precedence Violation -> Communication with destination administratively prohibited
|
||||||
|
// IcmpCode(15) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Precedence cutoff in effect -> Communication with destination administratively prohibited
|
||||||
|
// _ => Some((Icmpv6Type(1), Icmpv6Code(0), payload)),
|
||||||
|
// },
|
||||||
|
|
||||||
|
// // Time Exceeded
|
||||||
|
// IcmpType(11) => Some((Icmpv6Type(3), Icmpv6Code(icmp_code.0), {
|
||||||
|
// // The payload contains an IPv4 header and 8 bytes of data. This must also be translated
|
||||||
|
// let embedded_ipv4_packet = Ipv4Packet::new(&payload[4..]).unwrap();
|
||||||
|
// log::debug!("Embedded payload is: {:?}", embedded_ipv4_packet.payload());
|
||||||
|
// log::debug!(
|
||||||
|
// "Embedded next level protocol is: {}",
|
||||||
|
// embedded_ipv4_packet.get_next_level_protocol().0
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Build an IPv6 packet out of the IPv4 packet
|
||||||
|
// let mut embedded_ipv6_packet =
|
||||||
|
// MutableIpv6Packet::owned(vec![0u8; 40 + 4 + 8])
|
||||||
|
// .unwrap();
|
||||||
|
// embedded_ipv6_packet.set_version(6);
|
||||||
|
// embedded_ipv6_packet.set_source(new_source);
|
||||||
|
// embedded_ipv6_packet.set_destination(new_destination);
|
||||||
|
// embedded_ipv6_packet.set_hop_limit(embedded_ipv4_packet.get_ttl());
|
||||||
|
// embedded_ipv6_packet.set_next_header(
|
||||||
|
// match embedded_ipv4_packet.get_next_level_protocol() {
|
||||||
|
// IpNextHeaderProtocols::Icmp => IpNextHeaderProtocols::Icmpv6,
|
||||||
|
// proto => proto,
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// // embedded_ipv6_packet.set_payload_length(embedded_ipv4_packet.payload().len() as u16);
|
||||||
|
// embedded_ipv6_packet.set_payload_length(8u16);
|
||||||
|
|
||||||
|
// // Handle translating the embedded packet if it's ICMP
|
||||||
|
// match embedded_ipv4_packet.get_next_level_protocol() {
|
||||||
|
// IpNextHeaderProtocols::Icmp => {
|
||||||
|
// let embedded_ipv4_packet_payload_bytes = embedded_ipv4_packet.payload();
|
||||||
|
// let embedded_icmp_type = IcmpType(embedded_ipv4_packet_payload_bytes[0]);
|
||||||
|
// let embedded_icmp_code = IcmpCode(embedded_ipv4_packet_payload_bytes[1]);
|
||||||
|
// let embedded_icmp_payload = &embedded_ipv4_packet_payload_bytes[4..];
|
||||||
|
|
||||||
|
// // Translate from ICMP to ICMPv6
|
||||||
|
// let (embedded_icmpv6_type, embedded_icmpv6_code, embedded_icmpv6_payload) =
|
||||||
|
// remap_values_4to6(
|
||||||
|
// embedded_icmp_type,
|
||||||
|
// embedded_icmp_code,
|
||||||
|
// new_source,
|
||||||
|
// new_destination,
|
||||||
|
// embedded_icmp_payload.to_vec(),
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// // Build an ICMPv6 packet out of the ICMPv6 values
|
||||||
|
// let mut double_embedded_icmpv6_packet = MutableIcmpv6Packet::owned(vec![
|
||||||
|
// 0u8;
|
||||||
|
// Icmpv6Packet::minimum_packet_size()
|
||||||
|
// + embedded_icmpv6_payload.len()
|
||||||
|
// ])
|
||||||
|
// .unwrap();
|
||||||
|
// double_embedded_icmpv6_packet.set_icmpv6_type(embedded_icmpv6_type);
|
||||||
|
// double_embedded_icmpv6_packet.set_icmpv6_code(embedded_icmpv6_code);
|
||||||
|
// double_embedded_icmpv6_packet.set_payload(&embedded_icmpv6_payload);
|
||||||
|
// double_embedded_icmpv6_packet.set_checksum(0);
|
||||||
|
// double_embedded_icmpv6_packet.set_checksum(icmpv6::checksum(
|
||||||
|
// &double_embedded_icmpv6_packet.to_immutable(),
|
||||||
|
// &new_source,
|
||||||
|
// &new_destination,
|
||||||
|
// ));
|
||||||
|
|
||||||
|
// // Return the first 8 bytes of the embedded icmpv6 packet
|
||||||
|
// embedded_ipv6_packet.set_payload(&double_embedded_icmpv6_packet.packet()[..8]);
|
||||||
|
// }
|
||||||
|
// _ => embedded_ipv6_packet.set_payload(embedded_ipv4_packet.payload()),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Return the IPv6 packet
|
||||||
|
// embedded_ipv6_packet.packet().to_vec()
|
||||||
|
// })),
|
||||||
|
|
||||||
|
// // Echo Request
|
||||||
|
// IcmpType(8) => Some((Icmpv6Type(128), Icmpv6Code(0), payload)),
|
||||||
|
|
||||||
|
// // Echo Reply
|
||||||
|
// IcmpType(0) => Some((Icmpv6Type(129), Icmpv6Code(0), payload)),
|
||||||
|
|
||||||
|
// icmp_type => {
|
||||||
|
// log::warn!("ICMP type {} not supported", icmp_type.0);
|
||||||
|
// return None;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn remap_values_6to4(
|
||||||
|
// icmp_type: Icmpv6Type,
|
||||||
|
// icmp_code: Icmpv6Code,
|
||||||
|
// new_source: Ipv4Addr,
|
||||||
|
// new_destination: Ipv4Addr,
|
||||||
|
// payload: Vec<u8>,
|
||||||
|
// ) -> Option<(IcmpType, IcmpCode, Vec<u8>)> {
|
||||||
|
// match icmp_type {
|
||||||
|
// // Destination Unreachable
|
||||||
|
// Icmpv6Type(1) => match icmp_code {
|
||||||
|
// Icmpv6Code(0) => Some((IcmpType(3), IcmpCode(0), payload)), // No route to destination -> Destination network unreachable
|
||||||
|
// Icmpv6Code(3) => Some((IcmpType(3), IcmpCode(1), payload)), // Address unreachable -> Destination host unreachable
|
||||||
|
// Icmpv6Code(4) => Some((IcmpType(3), IcmpCode(3), payload)), // Port unreachable -> Destination port unreachable
|
||||||
|
// Icmpv6Code(5) => Some((IcmpType(3), IcmpCode(5), payload)), // Source route failed -> Source address failed ingress/egress policy
|
||||||
|
// Icmpv6Code(1) => Some((IcmpType(3), IcmpCode(13), payload)), // Communication administratively prohibited -> Communication administratively prohibited
|
||||||
|
// _ => Some((IcmpType(3), IcmpCode(0), payload)),
|
||||||
|
// },
|
||||||
|
|
||||||
|
// // Time Exceeded
|
||||||
|
// Icmpv6Type(3) => Some((IcmpType(11), IcmpCode(icmp_code.0), {
|
||||||
|
// // The payload contains an IPv6 header and 8 bytes of data. This must also be translated
|
||||||
|
// let embedded_ipv6_packet = Ipv6Packet::new(&payload).unwrap();
|
||||||
|
// log::debug!("Embedded payload is: {:?}", embedded_ipv6_packet.payload());
|
||||||
|
// log::debug!(
|
||||||
|
// "Embedded next header is: {}",
|
||||||
|
// embedded_ipv6_packet.get_next_header().0
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Build an IPv4 packet out of the IPv6 packet
|
||||||
|
// let mut embedded_ipv4_packet =
|
||||||
|
// MutableIpv4Packet::owned(vec![0u8; 20 + embedded_ipv6_packet.payload().len()])
|
||||||
|
// .unwrap();
|
||||||
|
// embedded_ipv4_packet.set_version(4);
|
||||||
|
// embedded_ipv4_packet.set_source(new_source);
|
||||||
|
// embedded_ipv4_packet.set_destination(new_destination);
|
||||||
|
// embedded_ipv4_packet.set_ttl(embedded_ipv6_packet.get_hop_limit());
|
||||||
|
// embedded_ipv4_packet.set_next_level_protocol(
|
||||||
|
// match embedded_ipv6_packet.get_next_header() {
|
||||||
|
// IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp,
|
||||||
|
// proto => proto,
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// embedded_ipv4_packet.set_header_length(5);
|
||||||
|
// embedded_ipv4_packet.set_total_length(20 + embedded_ipv6_packet.payload().len() as u16);
|
||||||
|
// embedded_ipv4_packet.set_payload(embedded_ipv6_packet.payload());
|
||||||
|
// embedded_ipv4_packet.set_checksum(0);
|
||||||
|
// embedded_ipv4_packet.set_checksum(ipv4::checksum(&embedded_ipv4_packet.to_immutable()));
|
||||||
|
|
||||||
|
// // Return the IPv4 packet
|
||||||
|
// embedded_ipv4_packet.packet().to_vec()
|
||||||
|
// })),
|
||||||
|
|
||||||
|
// // Echo Request
|
||||||
|
// Icmpv6Type(128) => Some((IcmpType(8), IcmpCode(0), payload)),
|
||||||
|
|
||||||
|
// // Echo Reply
|
||||||
|
// Icmpv6Type(129) => Some((IcmpType(0), IcmpCode(0), payload)),
|
||||||
|
|
||||||
|
// icmp_type => {
|
||||||
|
// log::warn!("ICMPv6 type {} not supported", icmp_type.0);
|
||||||
|
// return None;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[derive(Debug, thiserror::Error)]
|
||||||
|
// pub enum IcmpProxyError {
|
||||||
|
// #[error("Packet too short. Got {0} bytes")]
|
||||||
|
// PacketTooShort(usize),
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn proxy_icmp_packet<'a>(
|
||||||
|
// original_packet: IpPacket<'a>,
|
||||||
|
// new_source: IpAddr,
|
||||||
|
// new_destination: IpAddr,
|
||||||
|
// ) -> Result<Option<IpPacket>, IcmpProxyError> {
|
||||||
|
// // Parse the original packet's payload to extract ICMP data
|
||||||
|
// let icmp_packet = original_packet.get_payload().to_vec();
|
||||||
|
|
||||||
|
// // Construct a new output packet
|
||||||
|
// match (original_packet, new_source, new_destination) {
|
||||||
|
// // Translate IPv4(ICMP) to IPv6(ICMPv6)
|
||||||
|
// (IpPacket::V4(original_packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => {
|
||||||
|
// // Parse the ICMP packet
|
||||||
|
// let icmp_packet = IcmpPacket::new(&icmp_packet)
|
||||||
|
// .ok_or_else(|| IcmpProxyError::PacketTooShort(icmp_packet.len()))?;
|
||||||
|
// log::debug!(
|
||||||
|
// "Incoming packet has ICMP type: {}",
|
||||||
|
// icmp_packet.get_icmp_type().0
|
||||||
|
// );
|
||||||
|
// log::debug!(
|
||||||
|
// "Incoming packet has ICMP code: {}",
|
||||||
|
// icmp_packet.get_icmp_code().0
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Remap ICMP values to ICMPv6 ones
|
||||||
|
// if let Some((icmpv6_type, icmpv6_code, icmpv6_payload)) = remap_values_4to6(
|
||||||
|
// icmp_packet.get_icmp_type(),
|
||||||
|
// icmp_packet.get_icmp_code(),
|
||||||
|
// new_source,
|
||||||
|
// new_destination,
|
||||||
|
// icmp_packet.payload().to_vec(),
|
||||||
|
// ) {
|
||||||
|
// // Build an actual ICMPv6 packet out of the values
|
||||||
|
// let mut icmpv6_packet = MutableIcmpv6Packet::owned(vec![
|
||||||
|
// 0u8;
|
||||||
|
// Icmpv6Packet::minimum_packet_size()
|
||||||
|
// + icmpv6_payload.len()
|
||||||
|
// ])
|
||||||
|
// .unwrap();
|
||||||
|
// icmpv6_packet.set_icmpv6_type(icmpv6_type);
|
||||||
|
// icmpv6_packet.set_icmpv6_code(icmpv6_code);
|
||||||
|
// icmpv6_packet.set_payload(&icmpv6_payload);
|
||||||
|
// icmpv6_packet.set_checksum(0);
|
||||||
|
// icmpv6_packet.set_checksum(icmpv6::checksum(
|
||||||
|
// &icmpv6_packet.to_immutable(),
|
||||||
|
// &new_source,
|
||||||
|
// &new_destination,
|
||||||
|
// ));
|
||||||
|
|
||||||
|
// // Build an IPv6 packet out of the ICMPv6 packet
|
||||||
|
// let mut output =
|
||||||
|
// MutableIpv6Packet::owned(vec![0u8; 40 + icmpv6_packet.packet().len()]).unwrap();
|
||||||
|
// output.set_version(6);
|
||||||
|
// output.set_source(new_source);
|
||||||
|
// output.set_destination(new_destination);
|
||||||
|
// output.set_hop_limit(original_packet.get_ttl());
|
||||||
|
// output.set_next_header(IpNextHeaderProtocols::Icmpv6);
|
||||||
|
// output.set_payload_length(icmpv6_packet.packet().len() as u16);
|
||||||
|
// output.set_payload(icmpv6_packet.packet());
|
||||||
|
|
||||||
|
// // Return the IPv6 packet
|
||||||
|
// return Ok(Some(IpPacket::V6(
|
||||||
|
// Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
||||||
|
// )));
|
||||||
|
// }
|
||||||
|
// return Ok(None);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Translate IPv6(ICMPv6) to IPv4(ICMP)
|
||||||
|
// (IpPacket::V6(original_packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => {
|
||||||
|
// // Parse the ICMP packet
|
||||||
|
// let icmp_packet = Icmpv6Packet::new(&icmp_packet)
|
||||||
|
// .ok_or_else(|| IcmpProxyError::PacketTooShort(icmp_packet.len()))?;
|
||||||
|
// log::debug!(
|
||||||
|
// "Incoming packet has ICMPv6 type: {}",
|
||||||
|
// icmp_packet.get_icmpv6_type().0
|
||||||
|
// );
|
||||||
|
// log::debug!(
|
||||||
|
// "Incoming packet has ICMPv6 code: {}",
|
||||||
|
// icmp_packet.get_icmpv6_code().0
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Remap ICMPv6 values to ICMP ones
|
||||||
|
// if let Some((icmp_type, icmp_code, icmp_payload)) = remap_values_6to4(
|
||||||
|
// icmp_packet.get_icmpv6_type(),
|
||||||
|
// icmp_packet.get_icmpv6_code(),
|
||||||
|
// new_source,
|
||||||
|
// new_destination,
|
||||||
|
// icmp_packet.payload().to_vec(),
|
||||||
|
// ) {
|
||||||
|
// // Build an actual ICMP packet out of the values
|
||||||
|
// let mut icmp_packet = MutableIcmpPacket::owned(vec![
|
||||||
|
// 0u8;
|
||||||
|
// IcmpPacket::minimum_packet_size(
|
||||||
|
// ) + icmp_payload.len()
|
||||||
|
// ])
|
||||||
|
// .unwrap();
|
||||||
|
// icmp_packet.set_icmp_type(icmp_type);
|
||||||
|
// icmp_packet.set_icmp_code(icmp_code);
|
||||||
|
// icmp_packet.set_payload(&icmp_payload);
|
||||||
|
// icmp_packet.set_checksum(0);
|
||||||
|
// icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable()));
|
||||||
|
|
||||||
|
// // Build an IPv4 packet out of the ICMP packet
|
||||||
|
// let mut output =
|
||||||
|
// MutableIpv4Packet::owned(vec![0u8; 20 + icmp_packet.packet().len()]).unwrap();
|
||||||
|
// output.set_version(4);
|
||||||
|
// output.set_source(new_source);
|
||||||
|
// output.set_destination(new_destination);
|
||||||
|
// output.set_ttl(original_packet.get_hop_limit());
|
||||||
|
// output.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
|
||||||
|
// output.set_header_length(5);
|
||||||
|
// output.set_total_length(20 + icmp_packet.packet().len() as u16);
|
||||||
|
// output.set_payload(icmp_packet.packet());
|
||||||
|
// output.set_checksum(0);
|
||||||
|
// output.set_checksum(ipv4::checksum(&output.to_immutable()));
|
||||||
|
|
||||||
|
// // Return the IPv4 packet
|
||||||
|
// return Ok(Some(IpPacket::V4(
|
||||||
|
// Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
||||||
|
// )));
|
||||||
|
// }
|
||||||
|
// return Ok(None);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _ => unreachable!(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
use pnet_packet::{
|
use pnet_packet::{
|
||||||
icmp::{self, IcmpCode, IcmpPacket, IcmpType, MutableIcmpPacket},
|
icmp::{
|
||||||
icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, MutableIcmpv6Packet},
|
self, destination_unreachable, IcmpCode, IcmpPacket, IcmpType, IcmpTypes, MutableIcmpPacket,
|
||||||
ip::IpNextHeaderProtocols,
|
},
|
||||||
ipv4::{self, Ipv4Packet, MutableIpv4Packet},
|
icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, Icmpv6Types, MutableIcmpv6Packet},
|
||||||
ipv6::{Ipv6Packet, MutableIpv6Packet},
|
|
||||||
Packet,
|
Packet,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::nat::packet::IpPacket;
|
use super::PacketTranslationError;
|
||||||
|
|
||||||
fn remap_values_4to6(
|
/// Best effort translation from an ICMP type and code to an ICMPv6 type and code
|
||||||
|
fn translate_type_and_code_4_to_6(
|
||||||
icmp_type: IcmpType,
|
icmp_type: IcmpType,
|
||||||
icmp_code: IcmpCode,
|
icmp_code: IcmpCode,
|
||||||
new_source: Ipv6Addr,
|
) -> Option<(Icmpv6Type, Icmpv6Code)> {
|
||||||
new_destination: Ipv6Addr,
|
match (icmp_type, icmp_code) {
|
||||||
payload: Vec<u8>,
|
|
||||||
) -> Option<(Icmpv6Type, Icmpv6Code, Vec<u8>)> {
|
|
||||||
match icmp_type {
|
|
||||||
// Destination Unreachable
|
|
||||||
IcmpType(3) => match icmp_code {
|
|
||||||
IcmpCode(0) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination network unreachable -> No route to destination
|
|
||||||
IcmpCode(1) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Destination host unreachable -> Address unreachable
|
|
||||||
IcmpCode(2) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination protocol unreachable -> No route to destination
|
|
||||||
IcmpCode(3) => Some((Icmpv6Type(1), Icmpv6Code(4), payload)), // Destination port unreachable -> Port unreachable
|
|
||||||
IcmpCode(4) => Some((Icmpv6Type(2), Icmpv6Code(0), vec![])), // Fragmentation required, and DF flag set -> Packet too big
|
|
||||||
IcmpCode(5) => Some((Icmpv6Type(1), Icmpv6Code(5), payload)), // Source route failed -> Source address failed ingress/egress policy
|
|
||||||
IcmpCode(6) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination network unknown -> No route to destination
|
|
||||||
IcmpCode(7) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Destination host unknown -> Address unreachable
|
|
||||||
IcmpCode(8) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Source host isolated -> No route to destination
|
|
||||||
IcmpCode(9) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Network administratively prohibited -> Communication with destination administratively prohibited
|
|
||||||
IcmpCode(10) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Host administratively prohibited -> Communication with destination administratively prohibited
|
|
||||||
IcmpCode(11) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Network unreachable for ToS -> No route to destination
|
|
||||||
IcmpCode(12) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Host unreachable for ToS -> Address unreachable
|
|
||||||
IcmpCode(13) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Communication administratively prohibited -> Communication with destination administratively prohibited
|
|
||||||
IcmpCode(14) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Host Precedence Violation -> Communication with destination administratively prohibited
|
|
||||||
IcmpCode(15) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Precedence cutoff in effect -> Communication with destination administratively prohibited
|
|
||||||
_ => Some((Icmpv6Type(1), Icmpv6Code(0), payload)),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Time Exceeded
|
|
||||||
IcmpType(11) => Some((Icmpv6Type(3), Icmpv6Code(icmp_code.0), {
|
|
||||||
// The payload contains an IPv4 header and 8 bytes of data. This must also be translated
|
|
||||||
let embedded_ipv4_packet = Ipv4Packet::new(&payload[4..]).unwrap();
|
|
||||||
log::debug!("Embedded payload is: {:?}", embedded_ipv4_packet.payload());
|
|
||||||
log::debug!(
|
|
||||||
"Embedded next level protocol is: {}",
|
|
||||||
embedded_ipv4_packet.get_next_level_protocol().0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build an IPv6 packet out of the IPv4 packet
|
|
||||||
let mut embedded_ipv6_packet =
|
|
||||||
MutableIpv6Packet::owned(vec![0u8; 40 + 4 + 8])
|
|
||||||
.unwrap();
|
|
||||||
embedded_ipv6_packet.set_version(6);
|
|
||||||
embedded_ipv6_packet.set_source(new_source);
|
|
||||||
embedded_ipv6_packet.set_destination(new_destination);
|
|
||||||
embedded_ipv6_packet.set_hop_limit(embedded_ipv4_packet.get_ttl());
|
|
||||||
embedded_ipv6_packet.set_next_header(
|
|
||||||
match embedded_ipv4_packet.get_next_level_protocol() {
|
|
||||||
IpNextHeaderProtocols::Icmp => IpNextHeaderProtocols::Icmpv6,
|
|
||||||
proto => proto,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// embedded_ipv6_packet.set_payload_length(embedded_ipv4_packet.payload().len() as u16);
|
|
||||||
embedded_ipv6_packet.set_payload_length(8u16);
|
|
||||||
|
|
||||||
// Handle translating the embedded packet if it's ICMP
|
|
||||||
match embedded_ipv4_packet.get_next_level_protocol() {
|
|
||||||
IpNextHeaderProtocols::Icmp => {
|
|
||||||
let embedded_ipv4_packet_payload_bytes = embedded_ipv4_packet.payload();
|
|
||||||
let embedded_icmp_type = IcmpType(embedded_ipv4_packet_payload_bytes[0]);
|
|
||||||
let embedded_icmp_code = IcmpCode(embedded_ipv4_packet_payload_bytes[1]);
|
|
||||||
let embedded_icmp_payload = &embedded_ipv4_packet_payload_bytes[4..];
|
|
||||||
|
|
||||||
// Translate from ICMP to ICMPv6
|
|
||||||
let (embedded_icmpv6_type, embedded_icmpv6_code, embedded_icmpv6_payload) =
|
|
||||||
remap_values_4to6(
|
|
||||||
embedded_icmp_type,
|
|
||||||
embedded_icmp_code,
|
|
||||||
new_source,
|
|
||||||
new_destination,
|
|
||||||
embedded_icmp_payload.to_vec(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Build an ICMPv6 packet out of the ICMPv6 values
|
|
||||||
let mut double_embedded_icmpv6_packet = MutableIcmpv6Packet::owned(vec![
|
|
||||||
0u8;
|
|
||||||
Icmpv6Packet::minimum_packet_size()
|
|
||||||
+ embedded_icmpv6_payload.len()
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
double_embedded_icmpv6_packet.set_icmpv6_type(embedded_icmpv6_type);
|
|
||||||
double_embedded_icmpv6_packet.set_icmpv6_code(embedded_icmpv6_code);
|
|
||||||
double_embedded_icmpv6_packet.set_payload(&embedded_icmpv6_payload);
|
|
||||||
double_embedded_icmpv6_packet.set_checksum(0);
|
|
||||||
double_embedded_icmpv6_packet.set_checksum(icmpv6::checksum(
|
|
||||||
&double_embedded_icmpv6_packet.to_immutable(),
|
|
||||||
&new_source,
|
|
||||||
&new_destination,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Return the first 8 bytes of the embedded icmpv6 packet
|
|
||||||
embedded_ipv6_packet.set_payload(&double_embedded_icmpv6_packet.packet()[..8]);
|
|
||||||
}
|
|
||||||
_ => embedded_ipv6_packet.set_payload(embedded_ipv4_packet.payload()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return the IPv6 packet
|
|
||||||
embedded_ipv6_packet.packet().to_vec()
|
|
||||||
})),
|
|
||||||
|
|
||||||
// Echo Request
|
// Echo Request
|
||||||
IcmpType(8) => Some((Icmpv6Type(128), Icmpv6Code(0), payload)),
|
(IcmpTypes::EchoRequest, _) => Some((Icmpv6Types::EchoRequest, Icmpv6Code(0))),
|
||||||
|
|
||||||
// Echo Reply
|
// Echo Reply
|
||||||
IcmpType(0) => Some((Icmpv6Type(129), Icmpv6Code(0), payload)),
|
(IcmpTypes::EchoReply, _) => Some((Icmpv6Types::EchoReply, Icmpv6Code(0))),
|
||||||
|
|
||||||
icmp_type => {
|
// Packet Too Big
|
||||||
log::warn!("ICMP type {} not supported", icmp_type.0);
|
(
|
||||||
return None;
|
IcmpTypes::DestinationUnreachable,
|
||||||
|
destination_unreachable::IcmpCodes::FragmentationRequiredAndDFFlagSet,
|
||||||
|
) => Some((Icmpv6Types::PacketTooBig, Icmpv6Code(0))),
|
||||||
|
|
||||||
|
// Destination Unreachable
|
||||||
|
(IcmpTypes::DestinationUnreachable, icmp_code) => Some((
|
||||||
|
Icmpv6Types::DestinationUnreachable,
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
Icmpv6Code(match icmp_code {
|
||||||
|
destination_unreachable::IcmpCodes::DestinationHostUnreachable => 3,
|
||||||
|
destination_unreachable::IcmpCodes::DestinationProtocolUnreachable => 4,
|
||||||
|
destination_unreachable::IcmpCodes::DestinationPortUnreachable => 4,
|
||||||
|
destination_unreachable::IcmpCodes::SourceRouteFailed => 5,
|
||||||
|
destination_unreachable::IcmpCodes::SourceHostIsolated => 2,
|
||||||
|
destination_unreachable::IcmpCodes::NetworkAdministrativelyProhibited => 1,
|
||||||
|
destination_unreachable::IcmpCodes::HostAdministrativelyProhibited => 1,
|
||||||
|
destination_unreachable::IcmpCodes::CommunicationAdministrativelyProhibited => 1,
|
||||||
|
|
||||||
|
// Default to No Route to Destination
|
||||||
|
_ => 0,
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
|
||||||
|
// Time Exceeded
|
||||||
|
(IcmpTypes::TimeExceeded, icmp_code) => {
|
||||||
|
Some((Icmpv6Types::TimeExceeded, Icmpv6Code(icmp_code.0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default unsupported
|
||||||
|
_ => {
|
||||||
|
log::warn!(
|
||||||
|
"Unsupported ICMP code and type: {:?}, {:?}",
|
||||||
|
icmp_type,
|
||||||
|
icmp_code
|
||||||
|
);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remap_values_6to4(
|
/// Best effort translation from an ICMPv6 type and code to an ICMP type and code
|
||||||
|
fn translate_type_and_code_6_to_4(
|
||||||
icmp_type: Icmpv6Type,
|
icmp_type: Icmpv6Type,
|
||||||
icmp_code: Icmpv6Code,
|
icmp_code: Icmpv6Code,
|
||||||
new_source: Ipv4Addr,
|
) -> Option<(IcmpType, IcmpCode)> {
|
||||||
new_destination: Ipv4Addr,
|
match (icmp_type, icmp_code) {
|
||||||
payload: Vec<u8>,
|
|
||||||
) -> Option<(IcmpType, IcmpCode, Vec<u8>)> {
|
|
||||||
match icmp_type {
|
|
||||||
// Destination Unreachable
|
|
||||||
Icmpv6Type(1) => match icmp_code {
|
|
||||||
Icmpv6Code(0) => Some((IcmpType(3), IcmpCode(0), payload)), // No route to destination -> Destination network unreachable
|
|
||||||
Icmpv6Code(3) => Some((IcmpType(3), IcmpCode(1), payload)), // Address unreachable -> Destination host unreachable
|
|
||||||
Icmpv6Code(4) => Some((IcmpType(3), IcmpCode(3), payload)), // Port unreachable -> Destination port unreachable
|
|
||||||
Icmpv6Code(5) => Some((IcmpType(3), IcmpCode(5), payload)), // Source route failed -> Source address failed ingress/egress policy
|
|
||||||
Icmpv6Code(1) => Some((IcmpType(3), IcmpCode(13), payload)), // Communication administratively prohibited -> Communication administratively prohibited
|
|
||||||
_ => Some((IcmpType(3), IcmpCode(0), payload)),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Time Exceeded
|
|
||||||
Icmpv6Type(3) => Some((IcmpType(11), IcmpCode(icmp_code.0), {
|
|
||||||
// The payload contains an IPv6 header and 8 bytes of data. This must also be translated
|
|
||||||
let embedded_ipv6_packet = Ipv6Packet::new(&payload).unwrap();
|
|
||||||
log::debug!("Embedded payload is: {:?}", embedded_ipv6_packet.payload());
|
|
||||||
log::debug!(
|
|
||||||
"Embedded next header is: {}",
|
|
||||||
embedded_ipv6_packet.get_next_header().0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build an IPv4 packet out of the IPv6 packet
|
|
||||||
let mut embedded_ipv4_packet =
|
|
||||||
MutableIpv4Packet::owned(vec![0u8; 20 + embedded_ipv6_packet.payload().len()])
|
|
||||||
.unwrap();
|
|
||||||
embedded_ipv4_packet.set_version(4);
|
|
||||||
embedded_ipv4_packet.set_source(new_source);
|
|
||||||
embedded_ipv4_packet.set_destination(new_destination);
|
|
||||||
embedded_ipv4_packet.set_ttl(embedded_ipv6_packet.get_hop_limit());
|
|
||||||
embedded_ipv4_packet.set_next_level_protocol(
|
|
||||||
match embedded_ipv6_packet.get_next_header() {
|
|
||||||
IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp,
|
|
||||||
proto => proto,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
embedded_ipv4_packet.set_header_length(5);
|
|
||||||
embedded_ipv4_packet.set_total_length(20 + embedded_ipv6_packet.payload().len() as u16);
|
|
||||||
embedded_ipv4_packet.set_payload(embedded_ipv6_packet.payload());
|
|
||||||
embedded_ipv4_packet.set_checksum(0);
|
|
||||||
embedded_ipv4_packet.set_checksum(ipv4::checksum(&embedded_ipv4_packet.to_immutable()));
|
|
||||||
|
|
||||||
// Return the IPv4 packet
|
|
||||||
embedded_ipv4_packet.packet().to_vec()
|
|
||||||
})),
|
|
||||||
|
|
||||||
// Echo Request
|
// Echo Request
|
||||||
Icmpv6Type(128) => Some((IcmpType(8), IcmpCode(0), payload)),
|
(Icmpv6Types::EchoRequest, _) => Some((IcmpTypes::EchoRequest, IcmpCode(0))),
|
||||||
|
|
||||||
// Echo Reply
|
// Echo Reply
|
||||||
Icmpv6Type(129) => Some((IcmpType(0), IcmpCode(0), payload)),
|
(Icmpv6Types::EchoReply, _) => Some((IcmpTypes::EchoReply, IcmpCode(0))),
|
||||||
|
|
||||||
icmp_type => {
|
// Packet Too Big
|
||||||
log::warn!("ICMPv6 type {} not supported", icmp_type.0);
|
(Icmpv6Types::PacketTooBig, _) => Some((
|
||||||
return None;
|
IcmpTypes::DestinationUnreachable,
|
||||||
|
destination_unreachable::IcmpCodes::FragmentationRequiredAndDFFlagSet,
|
||||||
|
)),
|
||||||
|
|
||||||
|
// Destination Unreachable
|
||||||
|
(Icmpv6Types::DestinationUnreachable, icmp_code) => Some((
|
||||||
|
IcmpTypes::DestinationUnreachable,
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
match icmp_code.0 {
|
||||||
|
1 => destination_unreachable::IcmpCodes::CommunicationAdministrativelyProhibited,
|
||||||
|
2 => destination_unreachable::IcmpCodes::SourceHostIsolated,
|
||||||
|
3 => destination_unreachable::IcmpCodes::DestinationHostUnreachable,
|
||||||
|
4 => destination_unreachable::IcmpCodes::DestinationPortUnreachable,
|
||||||
|
5 => destination_unreachable::IcmpCodes::SourceRouteFailed,
|
||||||
|
_ => destination_unreachable::IcmpCodes::DestinationNetworkUnreachable,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
|
||||||
|
// Time Exceeded
|
||||||
|
(Icmpv6Types::TimeExceeded, icmp_code) => {
|
||||||
|
Some((IcmpTypes::TimeExceeded, IcmpCode(icmp_code.0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default unsupported
|
||||||
|
_ => {
|
||||||
|
log::warn!(
|
||||||
|
"Unsupported ICMPv6 code and type: {:?}, {:?}",
|
||||||
|
icmp_type,
|
||||||
|
icmp_code
|
||||||
|
);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
/// Translate an ICMP packet into an ICMPv6 packet
|
||||||
pub enum IcmpProxyError {
|
pub fn translate_icmp_4_to_6(
|
||||||
#[error("Packet too short. Got {0} bytes")]
|
icmp_packet: IcmpPacket,
|
||||||
PacketTooShort(usize),
|
new_source: Ipv6Addr,
|
||||||
}
|
new_dest: Ipv6Addr,
|
||||||
|
) -> Result<Option<IcmpPacket>, PacketTranslationError> {
|
||||||
|
// Translate the type and code
|
||||||
|
if let Some((icmpv6_type, icmpv6_code)) =
|
||||||
|
translate_type_and_code_4_to_6(icmp_packet.get_icmp_type(), icmp_packet.get_icmp_code())
|
||||||
|
{
|
||||||
|
// Time Exceeded packets require an extra 48 bytes of data
|
||||||
|
let payload_size = match icmpv6_type {
|
||||||
|
Icmpv6Types::TimeExceeded => 48,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn proxy_icmp_packet<'a>(
|
// Create a new ICMPv6 packet for the translated values to be stored in
|
||||||
original_packet: IpPacket<'a>,
|
let mut output = MutableIcmpv6Packet::owned(vec![
|
||||||
new_source: IpAddr,
|
|
||||||
new_destination: IpAddr,
|
|
||||||
) -> Result<Option<IpPacket>, IcmpProxyError> {
|
|
||||||
// Parse the original packet's payload to extract ICMP data
|
|
||||||
let icmp_packet = original_packet.get_payload().to_vec();
|
|
||||||
|
|
||||||
// Construct a new output packet
|
|
||||||
match (original_packet, new_source, new_destination) {
|
|
||||||
// Translate IPv4(ICMP) to IPv6(ICMPv6)
|
|
||||||
(IpPacket::V4(original_packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => {
|
|
||||||
// Parse the ICMP packet
|
|
||||||
let icmp_packet = IcmpPacket::new(&icmp_packet)
|
|
||||||
.ok_or_else(|| IcmpProxyError::PacketTooShort(icmp_packet.len()))?;
|
|
||||||
log::debug!(
|
|
||||||
"Incoming packet has ICMP type: {}",
|
|
||||||
icmp_packet.get_icmp_type().0
|
|
||||||
);
|
|
||||||
log::debug!(
|
|
||||||
"Incoming packet has ICMP code: {}",
|
|
||||||
icmp_packet.get_icmp_code().0
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remap ICMP values to ICMPv6 ones
|
|
||||||
if let Some((icmpv6_type, icmpv6_code, icmpv6_payload)) = remap_values_4to6(
|
|
||||||
icmp_packet.get_icmp_type(),
|
|
||||||
icmp_packet.get_icmp_code(),
|
|
||||||
new_source,
|
|
||||||
new_destination,
|
|
||||||
icmp_packet.payload().to_vec(),
|
|
||||||
) {
|
|
||||||
// Build an actual ICMPv6 packet out of the values
|
|
||||||
let mut icmpv6_packet = MutableIcmpv6Packet::owned(vec![
|
|
||||||
0u8;
|
0u8;
|
||||||
Icmpv6Packet::minimum_packet_size()
|
Icmpv6Packet::minimum_packet_size()
|
||||||
+ icmpv6_payload.len()
|
+ payload_size
|
||||||
])
|
])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
icmpv6_packet.set_icmpv6_type(icmpv6_type);
|
|
||||||
icmpv6_packet.set_icmpv6_code(icmpv6_code);
|
// Set the type and code
|
||||||
icmpv6_packet.set_payload(&icmpv6_payload);
|
output.set_icmpv6_type(icmpv6_type);
|
||||||
icmpv6_packet.set_checksum(0);
|
output.set_icmpv6_code(icmpv6_code);
|
||||||
icmpv6_packet.set_checksum(icmpv6::checksum(
|
|
||||||
&icmpv6_packet.to_immutable(),
|
// Calculate the checksum
|
||||||
|
output.set_checksum(0);
|
||||||
|
output.set_checksum(icmpv6::checksum(
|
||||||
|
&output.to_immutable(),
|
||||||
&new_source,
|
&new_source,
|
||||||
&new_destination,
|
&new_dest,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Build an IPv6 packet out of the ICMPv6 packet
|
//TODO: Add a payload if necessary
|
||||||
let mut output =
|
|
||||||
MutableIpv6Packet::owned(vec![0u8; 40 + icmpv6_packet.packet().len()]).unwrap();
|
|
||||||
output.set_version(6);
|
|
||||||
output.set_source(new_source);
|
|
||||||
output.set_destination(new_destination);
|
|
||||||
output.set_hop_limit(original_packet.get_ttl());
|
|
||||||
output.set_next_header(IpNextHeaderProtocols::Icmpv6);
|
|
||||||
output.set_payload_length(icmpv6_packet.packet().len() as u16);
|
|
||||||
output.set_payload(icmpv6_packet.packet());
|
|
||||||
|
|
||||||
// Return the IPv6 packet
|
// Return the translated packet
|
||||||
return Ok(Some(IpPacket::V6(
|
return Ok(Some(
|
||||||
Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
IcmpPacket::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
||||||
)));
|
));
|
||||||
}
|
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate IPv6(ICMPv6) to IPv4(ICMP)
|
Ok(None)
|
||||||
(IpPacket::V6(original_packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => {
|
}
|
||||||
// Parse the ICMP packet
|
|
||||||
let icmp_packet = Icmpv6Packet::new(&icmp_packet)
|
/// Translate an ICMPv6 packet into an ICMP packet
|
||||||
.ok_or_else(|| IcmpProxyError::PacketTooShort(icmp_packet.len()))?;
|
pub fn translate_icmp_6_to_4(
|
||||||
log::debug!(
|
icmpv6_packet: Icmpv6Packet,
|
||||||
"Incoming packet has ICMPv6 type: {}",
|
new_source: Ipv4Addr,
|
||||||
icmp_packet.get_icmpv6_type().0
|
new_dest: Ipv4Addr,
|
||||||
);
|
) -> Result<Option<Icmpv6Packet>, PacketTranslationError> {
|
||||||
log::debug!(
|
// Translate the type and code
|
||||||
"Incoming packet has ICMPv6 code: {}",
|
if let Some((icmp_type, icmp_code)) = translate_type_and_code_6_to_4(
|
||||||
icmp_packet.get_icmpv6_code().0
|
icmpv6_packet.get_icmpv6_type(),
|
||||||
);
|
icmpv6_packet.get_icmpv6_code(),
|
||||||
|
) {
|
||||||
// Remap ICMPv6 values to ICMP ones
|
// Time Exceeded packets require an extra 48 bytes of data
|
||||||
if let Some((icmp_type, icmp_code, icmp_payload)) = remap_values_6to4(
|
let payload_size = match icmp_type {
|
||||||
icmp_packet.get_icmpv6_type(),
|
IcmpTypes::TimeExceeded => 48,
|
||||||
icmp_packet.get_icmpv6_code(),
|
_ => 0,
|
||||||
new_source,
|
};
|
||||||
new_destination,
|
|
||||||
icmp_packet.payload().to_vec(),
|
// Create a new ICMP packet for the translated values to be stored in
|
||||||
) {
|
let mut output =
|
||||||
// Build an actual ICMP packet out of the values
|
MutableIcmpPacket::owned(vec![0u8; IcmpPacket::minimum_packet_size() + payload_size])
|
||||||
let mut icmp_packet = MutableIcmpPacket::owned(vec![
|
.unwrap();
|
||||||
0u8;
|
|
||||||
IcmpPacket::minimum_packet_size(
|
// Set the type and code
|
||||||
) + icmp_payload.len()
|
output.set_icmp_type(icmp_type);
|
||||||
])
|
output.set_icmp_code(icmp_code);
|
||||||
.unwrap();
|
|
||||||
icmp_packet.set_icmp_type(icmp_type);
|
// Calculate the checksum
|
||||||
icmp_packet.set_icmp_code(icmp_code);
|
output.set_checksum(0);
|
||||||
icmp_packet.set_payload(&icmp_payload);
|
output.set_checksum(icmp::checksum(&output.to_immutable()));
|
||||||
icmp_packet.set_checksum(0);
|
|
||||||
icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable()));
|
// TODO: Add a payload if necessary
|
||||||
|
|
||||||
// Build an IPv4 packet out of the ICMP packet
|
// Return the translated packet
|
||||||
let mut output =
|
return Ok(Some(
|
||||||
MutableIpv4Packet::owned(vec![0u8; 20 + icmp_packet.packet().len()]).unwrap();
|
Icmpv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
||||||
output.set_version(4);
|
));
|
||||||
output.set_source(new_source);
|
}
|
||||||
output.set_destination(new_destination);
|
|
||||||
output.set_ttl(original_packet.get_hop_limit());
|
Ok(None)
|
||||||
output.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
|
|
||||||
output.set_header_length(5);
|
|
||||||
output.set_total_length(20 + icmp_packet.packet().len() as u16);
|
|
||||||
output.set_payload(icmp_packet.packet());
|
|
||||||
output.set_checksum(0);
|
|
||||||
output.set_checksum(ipv4::checksum(&output.to_immutable()));
|
|
||||||
|
|
||||||
// Return the IPv4 packet
|
|
||||||
return Ok(Some(IpPacket::V4(
|
|
||||||
Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
//! Translation logic for IPv4 and IPv6
|
|
||||||
|
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
||||||
|
|
||||||
use pnet_packet::{ipv6::{Ipv6Packet, Ipv6, MutableIpv6Packet}, ipv4::{Ipv4, MutableIpv4Packet, self, Ipv4Packet}, Packet};
|
|
||||||
|
|
||||||
pub fn ipv6_to_ipv4(
|
|
||||||
ipv6_packet: &Ipv6Packet,
|
|
||||||
new_source: Ipv4Addr,
|
|
||||||
new_dest: Ipv4Addr,
|
|
||||||
decr_ttl: bool,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let data = Ipv4 {
|
|
||||||
version: 4,
|
|
||||||
header_length: 20,
|
|
||||||
dscp: 0,
|
|
||||||
ecn: 0,
|
|
||||||
total_length: 20 + ipv6_packet.payload().len() as u16,
|
|
||||||
identification: 0,
|
|
||||||
flags: 0,
|
|
||||||
fragment_offset: 0,
|
|
||||||
ttl: ipv6_packet.get_hop_limit(),
|
|
||||||
next_level_protocol: ipv6_packet.get_next_header(),
|
|
||||||
checksum: 0,
|
|
||||||
source: new_source,
|
|
||||||
destination: new_dest,
|
|
||||||
options: vec![],
|
|
||||||
payload: ipv6_packet.payload().to_vec(),
|
|
||||||
};
|
|
||||||
let mut packet = MutableIpv4Packet::owned(vec![0; 20 + ipv6_packet.payload().len()]).unwrap();
|
|
||||||
packet.populate(&data);
|
|
||||||
packet.set_checksum(ipv4::checksum(&packet.to_immutable()));
|
|
||||||
|
|
||||||
// Decrement the TTL if needed
|
|
||||||
if decr_ttl {
|
|
||||||
packet.set_ttl(packet.get_ttl() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut output = packet.to_immutable().packet().to_vec();
|
|
||||||
// TODO: There is a bug here.. for now, force write header size
|
|
||||||
output[0] = 0x45;
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ipv4_to_ipv6(
|
|
||||||
ipv4_packet: &Ipv4Packet,
|
|
||||||
new_source: Ipv6Addr,
|
|
||||||
new_dest: Ipv6Addr,
|
|
||||||
decr_ttl: bool,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let data = Ipv6 {
|
|
||||||
version: 6,
|
|
||||||
traffic_class: 0,
|
|
||||||
flow_label: 0,
|
|
||||||
payload_length: ipv4_packet.payload().len() as u16,
|
|
||||||
next_header: ipv4_packet.get_next_level_protocol(),
|
|
||||||
hop_limit: ipv4_packet.get_ttl(),
|
|
||||||
source: new_source,
|
|
||||||
destination: new_dest,
|
|
||||||
payload: ipv4_packet.payload().to_vec(),
|
|
||||||
};
|
|
||||||
let mut packet = MutableIpv6Packet::owned(vec![0; 40 + ipv4_packet.payload().len()]).unwrap();
|
|
||||||
packet.populate(&data);
|
|
||||||
|
|
||||||
// Decrement the TTL if needed
|
|
||||||
if decr_ttl {
|
|
||||||
packet.set_hop_limit(packet.get_hop_limit() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
packet.to_immutable().packet().to_vec()
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ mod icmp;
|
|||||||
mod tcp;
|
mod tcp;
|
||||||
mod udp;
|
mod udp;
|
||||||
|
|
||||||
pub use icmp::{proxy_icmp_packet, IcmpProxyError};
|
pub use icmp::{translate_icmp_4_to_6, translate_icmp_6_to_4};
|
||||||
pub use tcp::{translate_tcp_4_to_6, translate_tcp_6_to_4};
|
pub use tcp::{translate_tcp_4_to_6, translate_tcp_6_to_4};
|
||||||
pub use udp::{translate_udp_4_to_6, translate_udp_6_to_4};
|
pub use udp::{translate_udp_4_to_6, translate_udp_6_to_4};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user