diff --git a/src/nat/macros.rs b/src/nat/macros.rs index ecac203..14416ac 100644 --- a/src/nat/macros.rs +++ b/src/nat/macros.rs @@ -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 #[macro_export] macro_rules! into_udp { diff --git a/src/nat/mod.rs b/src/nat/mod.rs index a9b91c9..7af8f41 100644 --- a/src/nat/mod.rs +++ b/src/nat/mod.rs @@ -6,7 +6,9 @@ use std::{ use ipnet::{Ipv4Net, Ipv6Net}; 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::{ interface::Nat64Interface, @@ -157,6 +159,22 @@ impl Nat64 { match (packet, new_source, new_destination) { (IpPacket::V4(packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => { 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 IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V6(ipv6_packet!( new_source, @@ -194,6 +212,22 @@ impl Nat64 { } (IpPacket::V6(packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => { 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 IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V4(ipv4_packet!( new_source, diff --git a/src/nat/xlat/icmp.rs b/src/nat/xlat/icmp.rs index 1fb7398..d23c231 100644 --- a/src/nat/xlat/icmp.rs +++ b/src/nat/xlat/icmp.rs @@ -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, +// ) -> Option<(Icmpv6Type, Icmpv6Code, Vec)> { +// 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, +// ) -> Option<(IcmpType, IcmpCode, Vec)> { +// 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, 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::{ - icmp::{self, IcmpCode, IcmpPacket, IcmpType, MutableIcmpPacket}, - icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, MutableIcmpv6Packet}, - ip::IpNextHeaderProtocols, - ipv4::{self, Ipv4Packet, MutableIpv4Packet}, - ipv6::{Ipv6Packet, MutableIpv6Packet}, + icmp::{ + self, destination_unreachable, IcmpCode, IcmpPacket, IcmpType, IcmpTypes, MutableIcmpPacket, + }, + icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, Icmpv6Types, MutableIcmpv6Packet}, 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_code: IcmpCode, - new_source: Ipv6Addr, - new_destination: Ipv6Addr, - payload: Vec, -) -> Option<(Icmpv6Type, Icmpv6Code, Vec)> { - 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() - })), - +) -> Option<(Icmpv6Type, Icmpv6Code)> { + match (icmp_type, icmp_code) { // Echo Request - IcmpType(8) => Some((Icmpv6Type(128), Icmpv6Code(0), payload)), + (IcmpTypes::EchoRequest, _) => Some((Icmpv6Types::EchoRequest, Icmpv6Code(0))), // Echo Reply - IcmpType(0) => Some((Icmpv6Type(129), Icmpv6Code(0), payload)), + (IcmpTypes::EchoReply, _) => Some((Icmpv6Types::EchoReply, Icmpv6Code(0))), - icmp_type => { - log::warn!("ICMP type {} not supported", icmp_type.0); - return None; + // Packet Too Big + ( + 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_code: Icmpv6Code, - new_source: Ipv4Addr, - new_destination: Ipv4Addr, - payload: Vec, -) -> Option<(IcmpType, IcmpCode, Vec)> { - 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() - })), - +) -> Option<(IcmpType, IcmpCode)> { + match (icmp_type, icmp_code) { // Echo Request - Icmpv6Type(128) => Some((IcmpType(8), IcmpCode(0), payload)), + (Icmpv6Types::EchoRequest, _) => Some((IcmpTypes::EchoRequest, IcmpCode(0))), // Echo Reply - Icmpv6Type(129) => Some((IcmpType(0), IcmpCode(0), payload)), + (Icmpv6Types::EchoReply, _) => Some((IcmpTypes::EchoReply, IcmpCode(0))), - icmp_type => { - log::warn!("ICMPv6 type {} not supported", icmp_type.0); - return None; + // Packet Too Big + (Icmpv6Types::PacketTooBig, _) => Some(( + 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)] -pub enum IcmpProxyError { - #[error("Packet too short. Got {0} bytes")] - PacketTooShort(usize), -} +/// Translate an ICMP packet into an ICMPv6 packet +pub fn translate_icmp_4_to_6( + icmp_packet: IcmpPacket, + new_source: Ipv6Addr, + new_dest: Ipv6Addr, +) -> Result, 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>( - original_packet: IpPacket<'a>, - new_source: IpAddr, - new_destination: IpAddr, -) -> Result, IcmpProxyError> { - // Parse the original packet's payload to extract ICMP data - let icmp_packet = original_packet.get_payload().to_vec(); + // Create a new ICMPv6 packet for the translated values to be stored in + let mut output = MutableIcmpv6Packet::owned(vec![ + 0u8; + Icmpv6Packet::minimum_packet_size() + + payload_size + ]) + .unwrap(); - // 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 - ); + // Set the type and code + output.set_icmpv6_type(icmpv6_type); + output.set_icmpv6_code(icmpv6_code); - // 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, - )); + // Calculate the checksum + output.set_checksum(0); + output.set_checksum(icmpv6::checksum( + &output.to_immutable(), + &new_source, + &new_dest, + )); - // 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()); + //TODO: Add a payload if necessary - // 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!(), + // Return the translated packet + return Ok(Some( + IcmpPacket::owned(output.to_immutable().packet().to_vec()).unwrap(), + )); } + + Ok(None) +} + +/// Translate an ICMPv6 packet into an ICMP packet +pub fn translate_icmp_6_to_4( + icmpv6_packet: Icmpv6Packet, + new_source: Ipv4Addr, + new_dest: Ipv4Addr, +) -> Result, PacketTranslationError> { + // Translate the type and code + if let Some((icmp_type, icmp_code)) = translate_type_and_code_6_to_4( + icmpv6_packet.get_icmpv6_type(), + icmpv6_packet.get_icmpv6_code(), + ) { + // Time Exceeded packets require an extra 48 bytes of data + let payload_size = match icmp_type { + IcmpTypes::TimeExceeded => 48, + _ => 0, + }; + + // Create a new ICMP packet for the translated values to be stored in + let mut output = + MutableIcmpPacket::owned(vec![0u8; IcmpPacket::minimum_packet_size() + payload_size]) + .unwrap(); + + // Set the type and code + output.set_icmp_type(icmp_type); + output.set_icmp_code(icmp_code); + + // Calculate the checksum + output.set_checksum(0); + output.set_checksum(icmp::checksum(&output.to_immutable())); + + // TODO: Add a payload if necessary + + // Return the translated packet + return Ok(Some( + Icmpv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), + )); + } + + Ok(None) } diff --git a/src/nat/xlat/ip.rs b/src/nat/xlat/ip.rs deleted file mode 100644 index fb762d9..0000000 --- a/src/nat/xlat/ip.rs +++ /dev/null @@ -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 { - 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 { - 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() -} diff --git a/src/nat/xlat/mod.rs b/src/nat/xlat/mod.rs index 36eb800..cad1661 100644 --- a/src/nat/xlat/mod.rs +++ b/src/nat/xlat/mod.rs @@ -4,7 +4,7 @@ mod icmp; mod tcp; 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 udp::{translate_udp_4_to_6, translate_udp_6_to_4};