diff --git a/Makefile b/Makefile index e49d733..c585828 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SRC=$(wildcard src/*.rs) $(wildcard src/**/*.rs) $(wildcard src/**/**/*.rs) Cargo.toml target/debug/protomask: $(SRC) - cargo build + cross build --target x86_64-unknown-linux-musl sudo setcap cap_net_admin=eip $@ \ No newline at end of file diff --git a/src/nat/macros.rs b/src/nat/macros.rs index 14416ac..1b3a651 100644 --- a/src/nat/macros.rs +++ b/src/nat/macros.rs @@ -72,7 +72,7 @@ macro_rules! ipv6_packet { /// Quickly construct an IPv4 packet with the given parameters #[macro_export] macro_rules! ipv4_packet { - ($source:expr, $destination:expr, $ttl:expr, $next_level_protocol:expr, $payload:expr) => { + ($source:expr, $destination:expr, $next_level_protocol:expr, $ttl:expr, $payload:expr) => { ipv4_packet!( $source, $destination, @@ -121,3 +121,43 @@ macro_rules! ipv4_packet { pnet_packet::ipv4::Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap() }}; } + +/// Quickly construct an ICMPv6 packet with the given parameters +#[macro_export] +macro_rules! icmpv6_packet { + ($source:expr, $destination:expr, $message_type:expr, $code:expr) => { + icmpv6_packet!($source, $destination, $message_type, $code, &[0u8; 0]) + }; + ($source:expr, $destination:expr, $message_type:expr, $code:expr, $payload:expr) => {{ + let mut output = + pnet_packet::icmpv6::MutableIcmpv6Packet::owned(vec![0u8; 4 + $payload.len()]).unwrap(); + output.set_icmpv6_type($message_type); + output.set_icmpv6_code($code); + output.set_payload($payload); + output.set_checksum(0); + output.set_checksum(pnet_packet::icmpv6::checksum( + &output.to_immutable(), + &$source, + &$destination, + )); + pnet_packet::icmpv6::Icmpv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap() + }}; +} + +/// Quickly construct an ICMP packet with the given parameters +#[macro_export] +macro_rules! icmp_packet { + ($message_type:expr, $code:expr) => { + icmp_packet!($message_type, $code, &[0u8; 0]) + }; + ($message_type:expr, $code:expr, $payload:expr) => {{ + let mut output = + pnet_packet::icmp::MutableIcmpPacket::owned(vec![0u8; 4 + $payload.len()]).unwrap(); + output.set_icmp_type($message_type); + output.set_icmp_code($code); + output.set_payload($payload); + output.set_checksum(0); + output.set_checksum(pnet_packet::icmp::checksum(&output.to_immutable())); + pnet_packet::icmp::IcmpPacket::owned(output.to_immutable().packet().to_vec()).unwrap() + }}; +} diff --git a/src/nat/mod.rs b/src/nat/mod.rs index 7af8f41..f1c925a 100644 --- a/src/nat/mod.rs +++ b/src/nat/mod.rs @@ -7,7 +7,8 @@ use ipnet::{Ipv4Net, Ipv6Net}; use pnet_packet::{ip::IpNextHeaderProtocols, Packet}; use crate::{ - into_icmp, into_tcp, into_udp, ipv4_packet, ipv6_packet, nat::xlat::translate_udp_4_to_6, into_icmpv6, + into_icmp, into_icmpv6, into_tcp, into_udp, ipv4_packet, ipv6_packet, + nat::xlat::translate_udp_4_to_6, }; use self::{ @@ -75,10 +76,11 @@ impl Nat64 { match self.interface.recv(&mut buffer) { Ok(packet_len) => { // Parse in to a more friendly format + log::debug!("--- NEW PACKET ---"); match IpPacket::new(&buffer[..packet_len]) { // Try to process the packet Ok(inbound_packet) => match self.process_packet(inbound_packet).await { - Ok(inbound_packet) => match inbound_packet { + Ok(outbound_packet) => match outbound_packet { // If data is returned, send it back out the interface Some(outbound_packet) => { let packet_bytes = outbound_packet.to_bytes(); @@ -168,7 +170,7 @@ impl Nat64 { Some(icmp_packet) => Ok(Some(IpPacket::V6(ipv6_packet!( new_source, new_destination, - IpNextHeaderProtocols::Icmp, + IpNextHeaderProtocols::Icmpv6, packet.get_ttl(), icmp_packet.packet() )))), @@ -221,8 +223,8 @@ impl Nat64 { Some(icmp_packet) => Ok(Some(IpPacket::V4(ipv4_packet!( new_source, new_destination, - packet.get_hop_limit(), IpNextHeaderProtocols::Icmp, + packet.get_hop_limit(), icmp_packet.packet() )))), None => Ok(None), @@ -232,8 +234,8 @@ impl Nat64 { IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V4(ipv4_packet!( new_source, new_destination, - packet.get_hop_limit(), IpNextHeaderProtocols::Udp, + packet.get_hop_limit(), xlat::translate_udp_6_to_4( into_udp!(packet.payload().to_vec())?, new_source, @@ -246,8 +248,8 @@ impl Nat64 { IpNextHeaderProtocols::Tcp => Ok(Some(IpPacket::V4(ipv4_packet!( new_source, new_destination, - packet.get_hop_limit(), IpNextHeaderProtocols::Tcp, + packet.get_hop_limit(), xlat::translate_tcp_6_to_4( into_tcp!(packet.payload().to_vec())?, new_source, @@ -267,20 +269,5 @@ impl Nat64 { // Honestly, this should probably be `unreachable!()` _ => unimplemented!(), } - // match next_header_protocol { - // IpNextHeaderProtocols::Icmp | IpNextHeaderProtocols::Icmpv6 => Ok( - // xlat::proxy_icmp_packet(packet, new_source, new_destination)?, - // ), - // IpNextHeaderProtocols::Udp => Ok(Some( - // xlat::proxy_udp_packet(packet, new_source, new_destination).await?, - // )), - // IpNextHeaderProtocols::Tcp => Ok(Some( - // xlat::proxy_tcp_packet(packet, new_source, new_destination).await?, - // )), - // next_header_protocol => { - // log::warn!("Unsupported next header protocol: {}", next_header_protocol); - // Ok(None) - // } - // } } } diff --git a/src/nat/xlat/icmp.rs b/src/nat/xlat/icmp.rs index d23c231..f33d464 100644 --- a/src/nat/xlat/icmp.rs +++ b/src/nat/xlat/icmp.rs @@ -1,333 +1,3 @@ -// //! Translation logic for ICMP and ICMPv6 - -// 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::{ @@ -335,9 +5,14 @@ use pnet_packet::{ self, destination_unreachable, IcmpCode, IcmpPacket, IcmpType, IcmpTypes, MutableIcmpPacket, }, icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, Icmpv6Types, MutableIcmpv6Packet}, + ip::IpNextHeaderProtocols, + ipv4::Ipv4Packet, + ipv6::Ipv6Packet, Packet, }; +use crate::{icmpv6_packet, ipv4_packet, ipv6_packet, icmp_packet}; + use super::PacketTranslationError; /// Best effort translation from an ICMP type and code to an ICMPv6 type and code @@ -448,22 +123,66 @@ pub fn translate_icmp_4_to_6( icmp_packet: IcmpPacket, new_source: Ipv6Addr, new_dest: Ipv6Addr, -) -> Result, PacketTranslationError> { +) -> 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, - }; + // "Time Exceeded" requires an additional payload be embedded in the packet + // This payload looks like: 4bytes + IPv6(data) + let mut output_payload = vec![]; + if icmpv6_type == Icmpv6Types::TimeExceeded { + // Get access to the original payload + let original_payload = + Ipv4Packet::new(&icmp_packet.payload()[4..]).ok_or_else(|| { + PacketTranslationError::EmbeddedPacketTooShort(icmp_packet.payload().len() - 4) + })?; + + // Copy the original payload's payload to a buffer + let mut original_payload_inner = vec![0u8; original_payload.payload().len()]; + original_payload_inner.copy_from_slice(original_payload.payload()); + + // if the original payload's next header is ICMP, we need to translated the inner payload's ICMP type + if original_payload.get_next_level_protocol() == IpNextHeaderProtocols::Icmp { + log::debug!("Time Exceeded packet contains another ICMP packet.. Translating"); + if let Some((icmpv6_type, icmpv6_code)) = translate_type_and_code_4_to_6( + IcmpType(original_payload_inner[0]), + IcmpCode(original_payload_inner[1]), + ) { + let inner_icmpv6 = icmpv6_packet!( + new_source, + new_dest, + icmpv6_type, + icmpv6_code + ); + original_payload_inner = inner_icmpv6.packet().to_vec(); + log::debug!("Translated inner ICMPv6 packet: {:?}", original_payload_inner); + } + } + + // Build a new IPv6 packet out of the embedded IPv4 packet's data + let new_payload_packet = ipv6_packet!( + new_source, + new_dest, + match original_payload.get_next_level_protocol() { + IpNextHeaderProtocols::Icmp => IpNextHeaderProtocols::Icmpv6, + proto => proto, + }, + original_payload.get_ttl(), + &original_payload_inner + ); + + // Set the payload + let mut payload = vec![0u8; 4 + new_payload_packet.packet().len()]; + payload[4..].copy_from_slice(new_payload_packet.packet()); + output_payload = payload; + } // 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 + + output_payload.len() ]) .unwrap(); @@ -479,11 +198,12 @@ pub fn translate_icmp_4_to_6( &new_dest, )); - //TODO: Add a payload if necessary + // Set the payload + output.set_payload(&output_payload); // Return the translated packet return Ok(Some( - IcmpPacket::owned(output.to_immutable().packet().to_vec()).unwrap(), + Icmpv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), )); } @@ -495,22 +215,82 @@ pub fn translate_icmp_6_to_4( icmpv6_packet: Icmpv6Packet, new_source: Ipv4Addr, new_dest: Ipv4Addr, -) -> Result, PacketTranslationError> { +) -> Result, PacketTranslationError> { + // If the incoming packet is a "Parameter Problem", log it + if icmpv6_packet.get_icmpv6_type() == Icmpv6Types::ParameterProblem { + log::warn!( + "ICMPv6 Parameter Problem: {:?}", + match icmpv6_packet.get_icmpv6_code().0 { + 0 => "Erroneous header field encountered", + 1 => "Unrecognized Next Header type encountered", + 2 => "Unrecognized IPv6 option encountered", + _ => "Unknown", + } + ); + } + // 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, - }; + // "Time Exceeded" requires an additional payload be embedded in the packet + // This payload looks like: 4bytes + IPv6(8bytes) + let mut output_payload = vec![]; + if icmp_type == IcmpTypes::TimeExceeded { + // Get access to the original payload + let original_payload = + Ipv6Packet::new(&icmpv6_packet.payload()[4..]).ok_or_else(|| { + PacketTranslationError::EmbeddedPacketTooShort( + icmpv6_packet.payload().len() - 4, + ) + })?; + + // Copy the original payload's payload to a buffer + let mut original_payload_inner = vec![0u8; original_payload.payload().len()]; + original_payload_inner.copy_from_slice(original_payload.payload()); + + // if the original payload's next header is ICMPv6, we need to translated the inner payload's ICMPv6 type + if original_payload.get_next_header() == IpNextHeaderProtocols::Icmpv6 { + log::debug!("Time Exceeded packet contains another ICMPv6 packet.. Translating"); + if let Some((icmp_type, icmp_code)) = translate_type_and_code_6_to_4( + Icmpv6Type(original_payload_inner[0]), + Icmpv6Code(original_payload_inner[1]), + ) { + let inner_icmp = icmp_packet!( + icmp_type, + icmp_code + ); + original_payload_inner = inner_icmp.packet().to_vec(); + log::debug!("Translated inner ICMP packet: {:?}", original_payload_inner); + } + } + + // Build a new IPv6 packet out of the embedded IPv4 packet's data + let new_payload_packet = ipv4_packet!( + new_source, + new_dest, + match original_payload.get_next_header() { + IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp, + proto => proto, + }, + original_payload.get_hop_limit(), + &original_payload_inner[..std::cmp::min(8, original_payload_inner.len())] + ); + + // Set the payload + let mut payload = vec![0u8; 4 + new_payload_packet.packet().len()]; + payload[4..].copy_from_slice(new_payload_packet.packet()); + output_payload = payload; + } // 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(); + let mut output = MutableIcmpPacket::owned(vec![ + 0u8; + IcmpPacket::minimum_packet_size() + + output_payload.len() + ]) + .unwrap(); // Set the type and code output.set_icmp_type(icmp_type); @@ -520,11 +300,12 @@ pub fn translate_icmp_6_to_4( output.set_checksum(0); output.set_checksum(icmp::checksum(&output.to_immutable())); - // TODO: Add a payload if necessary + // Set the payload + output.set_payload(&output_payload); // Return the translated packet return Ok(Some( - Icmpv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), + IcmpPacket::owned(output.to_immutable().packet().to_vec()).unwrap(), )); } diff --git a/src/nat/xlat/mod.rs b/src/nat/xlat/mod.rs index cad1661..1c928a3 100644 --- a/src/nat/xlat/mod.rs +++ b/src/nat/xlat/mod.rs @@ -12,4 +12,6 @@ pub use udp::{translate_udp_4_to_6, translate_udp_6_to_4}; pub enum PacketTranslationError { #[error("Input packet too short. Got {0} bytes")] InputPacketTooShort(usize), + #[error("Embedded packet too short. Got {0} bytes")] + EmbeddedPacketTooShort(usize), }