From 94fdf28012ad24bc55984b504ad92ab5cfc02d6f Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Tue, 18 Jul 2023 16:06:53 -0400 Subject: [PATCH] Finish packet rewrite --- src/nat/macros.rs | 308 +++++++++++++++--------------- src/nat/mod.rs | 107 ++--------- src/nat/xlat/icmp.rs | 180 ++++++++--------- src/nat/xlat/mod.rs | 28 +-- src/nat/xlat/tcp.rs | 88 ++++----- src/nat/xlat/udp.rs | 202 ++++++++++---------- src/packet/error.rs | 4 + src/packet/mod.rs | 1 + src/packet/protocols/raw.rs | 6 + src/packet/protocols/tcp.rs | 9 +- src/packet/xlat/icmp/mod.rs | 92 +++++++++ src/packet/xlat/icmp/type_code.rs | 97 ++++++++++ src/packet/xlat/ip.rs | 129 +++++++++++++ src/packet/xlat/mod.rs | 6 + src/packet/xlat/tcp.rs | 46 +++++ src/packet/xlat/udp.rs | 34 ++++ 16 files changed, 838 insertions(+), 499 deletions(-) create mode 100644 src/packet/xlat/icmp/mod.rs create mode 100644 src/packet/xlat/icmp/type_code.rs create mode 100644 src/packet/xlat/ip.rs create mode 100644 src/packet/xlat/mod.rs create mode 100644 src/packet/xlat/tcp.rs create mode 100644 src/packet/xlat/udp.rs diff --git a/src/nat/macros.rs b/src/nat/macros.rs index 1b3a651..0edcb0d 100644 --- a/src/nat/macros.rs +++ b/src/nat/macros.rs @@ -1,163 +1,163 @@ -/// 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 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 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 { - ($bytes:expr) => { - pnet_packet::udp::UdpPacket::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 { +// ($bytes:expr) => { +// pnet_packet::udp::UdpPacket::owned($bytes).ok_or_else(|| { +// crate::nat::xlat::PacketTranslationError::InputPacketTooShort($bytes.len()) +// }) +// }; +// } -/// Quickly convert a byte slice into a TCP packet -#[macro_export] -macro_rules! into_tcp { - ($bytes:expr) => { - pnet_packet::tcp::TcpPacket::owned($bytes).ok_or_else(|| { - crate::nat::xlat::PacketTranslationError::InputPacketTooShort($bytes.len()) - }) - }; -} +// /// Quickly convert a byte slice into a TCP packet +// #[macro_export] +// macro_rules! into_tcp { +// ($bytes:expr) => { +// pnet_packet::tcp::TcpPacket::owned($bytes).ok_or_else(|| { +// crate::nat::xlat::PacketTranslationError::InputPacketTooShort($bytes.len()) +// }) +// }; +// } -/// Quickly construct an IPv6 packet with the given parameters -#[macro_export] -macro_rules! ipv6_packet { - ($source:expr, $destination:expr, $next_header:expr, $hop_limit:expr, $payload:expr) => { - ipv6_packet!( - $source, - $destination, - 0, - 0, - $next_header, - $hop_limit, - $payload - ) - }; +// /// Quickly construct an IPv6 packet with the given parameters +// #[macro_export] +// macro_rules! ipv6_packet { +// ($source:expr, $destination:expr, $next_header:expr, $hop_limit:expr, $payload:expr) => { +// ipv6_packet!( +// $source, +// $destination, +// 0, +// 0, +// $next_header, +// $hop_limit, +// $payload +// ) +// }; - ($source:expr, $destination:expr, $traffic_class:expr, $flow_label:expr, $next_header:expr, $hop_limit:expr, $payload:expr) => {{ - let mut output = - pnet_packet::ipv6::MutableIpv6Packet::owned(vec![0u8; 40 + $payload.len()]).unwrap(); - output.set_version(6); - output.set_traffic_class($traffic_class); - output.set_flow_label($flow_label); - output.set_next_header($next_header); - output.set_hop_limit($hop_limit); - output.set_source($source); - output.set_destination($destination); - output.set_payload_length($payload.len() as u16); - output.set_payload($payload); - pnet_packet::ipv6::Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap() - }}; -} +// ($source:expr, $destination:expr, $traffic_class:expr, $flow_label:expr, $next_header:expr, $hop_limit:expr, $payload:expr) => {{ +// let mut output = +// pnet_packet::ipv6::MutableIpv6Packet::owned(vec![0u8; 40 + $payload.len()]).unwrap(); +// output.set_version(6); +// output.set_traffic_class($traffic_class); +// output.set_flow_label($flow_label); +// output.set_next_header($next_header); +// output.set_hop_limit($hop_limit); +// output.set_source($source); +// output.set_destination($destination); +// output.set_payload_length($payload.len() as u16); +// output.set_payload($payload); +// pnet_packet::ipv6::Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap() +// }}; +// } -/// Quickly construct an IPv4 packet with the given parameters -#[macro_export] -macro_rules! ipv4_packet { - ($source:expr, $destination:expr, $next_level_protocol:expr, $ttl:expr, $payload:expr) => { - ipv4_packet!( - $source, - $destination, - 0, - 0, - 0, - 0, - 0, - $ttl, - $next_level_protocol, - // &[], - $payload - ) - }; +// /// Quickly construct an IPv4 packet with the given parameters +// #[macro_export] +// macro_rules! ipv4_packet { +// ($source:expr, $destination:expr, $next_level_protocol:expr, $ttl:expr, $payload:expr) => { +// ipv4_packet!( +// $source, +// $destination, +// 0, +// 0, +// 0, +// 0, +// 0, +// $ttl, +// $next_level_protocol, +// // &[], +// $payload +// ) +// }; - // NOTE: Temporarily disabled options, since we aren't using them - // ($source:expr, $destination:expr, $dscp:expr, $ecn:expr, $identification:expr, $flags:expr, $fragment_offset:expr, $ttl:expr, $next_level_protocol:expr, $options:expr, $payload:expr) => {{ - ($source:expr, $destination:expr, $dscp:expr, $ecn:expr, $identification:expr, $flags:expr, $fragment_offset:expr, $ttl:expr, $next_level_protocol:expr, $payload:expr) => {{ - // let total_option_length = $options - // .iter() - // .map(|o: pnet_packet::ipv4::Ipv4Option| pnet_packet::Packet::payload(o).len()) - // .sum::(); - let total_option_length: usize = 0; - let mut output = pnet_packet::ipv4::MutableIpv4Packet::owned(vec![ - 0u8; - 20 + total_option_length - + $payload.len() - ]) - .unwrap(); - output.set_version(4); - output.set_header_length(((20 + total_option_length) / (32 / 8)) as u8); // Dynamic header length :( - output.set_dscp($dscp); - output.set_ecn($ecn); - output.set_total_length((20 + total_option_length + $payload.len()) as u16); - output.set_identification($identification); - output.set_flags($flags); - output.set_fragment_offset($fragment_offset); - output.set_ttl($ttl); - output.set_next_level_protocol($next_level_protocol); - output.set_source($source); - output.set_destination($destination); - // output.set_options($options); - output.set_payload($payload); - output.set_checksum(0); - output.set_checksum(pnet_packet::ipv4::checksum(&output.to_immutable())); - pnet_packet::ipv4::Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap() - }}; -} +// // NOTE: Temporarily disabled options, since we aren't using them +// // ($source:expr, $destination:expr, $dscp:expr, $ecn:expr, $identification:expr, $flags:expr, $fragment_offset:expr, $ttl:expr, $next_level_protocol:expr, $options:expr, $payload:expr) => {{ +// ($source:expr, $destination:expr, $dscp:expr, $ecn:expr, $identification:expr, $flags:expr, $fragment_offset:expr, $ttl:expr, $next_level_protocol:expr, $payload:expr) => {{ +// // let total_option_length = $options +// // .iter() +// // .map(|o: pnet_packet::ipv4::Ipv4Option| pnet_packet::Packet::payload(o).len()) +// // .sum::(); +// let total_option_length: usize = 0; +// let mut output = pnet_packet::ipv4::MutableIpv4Packet::owned(vec![ +// 0u8; +// 20 + total_option_length +// + $payload.len() +// ]) +// .unwrap(); +// output.set_version(4); +// output.set_header_length(((20 + total_option_length) / (32 / 8)) as u8); // Dynamic header length :( +// output.set_dscp($dscp); +// output.set_ecn($ecn); +// output.set_total_length((20 + total_option_length + $payload.len()) as u16); +// output.set_identification($identification); +// output.set_flags($flags); +// output.set_fragment_offset($fragment_offset); +// output.set_ttl($ttl); +// output.set_next_level_protocol($next_level_protocol); +// output.set_source($source); +// output.set_destination($destination); +// // output.set_options($options); +// output.set_payload($payload); +// output.set_checksum(0); +// output.set_checksum(pnet_packet::ipv4::checksum(&output.to_immutable())); +// 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 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() - }}; -} +// /// 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 c2e70fb..2c48a51 100644 --- a/src/nat/mod.rs +++ b/src/nat/mod.rs @@ -1,6 +1,6 @@ -use crate::packet::protocols::{ - icmp::IcmpPacket, icmpv6::Icmpv6Packet, ipv4::Ipv4Packet, ipv6::Ipv6Packet, raw::RawBytes, - tcp::TcpPacket, udp::UdpPacket, +use crate::packet::{ + protocols::{ipv4::Ipv4Packet, ipv6::Ipv6Packet}, + xlat::ip::{translate_ipv4_to_ipv6, translate_ipv6_to_ipv4}, }; use self::{ @@ -8,7 +8,6 @@ use self::{ utils::{embed_address, extract_address}, }; use ipnet::{Ipv4Net, Ipv6Net}; -use pnet_packet::ip::IpNextHeaderProtocols; use protomask_tun::TunDevice; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, @@ -31,12 +30,14 @@ pub enum Nat64Error { TunError(#[from] protomask_tun::Error), #[error(transparent)] IoError(#[from] std::io::Error), - #[error(transparent)] - XlatError(#[from] xlat::PacketTranslationError), + // #[error(transparent)] + // XlatError(#[from] xlat::PacketTranslationError), #[error(transparent)] PacketHandlingError(#[from] crate::packet::error::PacketError), #[error(transparent)] PacketReceiveError(#[from] broadcast::error::RecvError), + #[error(transparent)] + PacketSendError(#[from] mpsc::error::SendError>), } pub struct Nat64 { @@ -88,7 +89,7 @@ impl Nat64 { let packet = rx.recv().await?; // Clone the TX so the worker can respond with data - let mut tx = tx.clone(); + let tx = tx.clone(); // Separate logic is needed for handling IPv4 vs IPv6 packets, so a check must be done here match packet[0] >> 4 { @@ -107,9 +108,9 @@ impl Nat64 { // Spawn a task to process the packet tokio::spawn(async move { - process_inbound_ipv4(packet, new_source, new_destination, &mut tx) - .await - .unwrap(); + let output = + translate_ipv4_to_ipv6(packet, new_source, new_destination).unwrap(); + tx.send(output.into()).await.unwrap(); }); } 6 => { @@ -122,9 +123,9 @@ impl Nat64 { // Spawn a task to process the packet tokio::spawn(async move { - process_inbound_ipv6(packet, new_source, new_destination, &mut tx) - .await - .unwrap(); + let output = + translate_ipv6_to_ipv4(packet, new_source, new_destination).unwrap(); + tx.send(output.into()).await.unwrap(); }); } n => { @@ -132,85 +133,5 @@ impl Nat64 { } } } - - Ok(()) } } - -/// Process an inbound IPv4 packet -async fn process_inbound_ipv4( - packet: Ipv4Packet>, - new_source: Ipv6Addr, - new_destination: Ipv6Addr, - tx: &mut mpsc::Sender>, -) -> Result<(), Nat64Error> { - // Handle each possible embedded packet type - match packet.protocol { - IpNextHeaderProtocols::Icmp => { - let icmp_packet: IcmpPacket = packet.payload.try_into()?; - todo!() - } - IpNextHeaderProtocols::Udp => { - let udp_packet: UdpPacket = UdpPacket::new_from_bytes_raw_payload( - &packet.payload, - IpAddr::V4(packet.source_address), - IpAddr::V4(packet.destination_address), - )?; - todo!() - } - IpNextHeaderProtocols::Tcp => { - let tcp_packet: TcpPacket = TcpPacket::new_from_bytes_raw_payload( - &packet.payload, - IpAddr::V4(packet.source_address), - IpAddr::V4(packet.destination_address), - )?; - todo!() - } - _ => { - log::warn!("Unsupported next level protocol: {}", packet.protocol); - } - } - - Ok(()) -} - -/// Process an inbound IPv6 packet -async fn process_inbound_ipv6( - packet: Ipv6Packet>, - new_source: Ipv4Addr, - new_destination: Ipv4Addr, - tx: &mut mpsc::Sender>, -) -> Result<(), Nat64Error> { - // Handle each possible embedded packet type - match packet.next_header { - IpNextHeaderProtocols::Icmpv6 => { - let icmpv6_packet: Icmpv6Packet = Icmpv6Packet::new_from_bytes_raw_payload( - &packet.payload, - packet.source_address, - packet.destination_address, - )?; - todo!() - } - IpNextHeaderProtocols::Udp => { - let udp_packet: UdpPacket = UdpPacket::new_from_bytes_raw_payload( - &packet.payload, - IpAddr::V6(packet.source_address), - IpAddr::V6(packet.destination_address), - )?; - todo!() - } - IpNextHeaderProtocols::Tcp => { - let tcp_packet: TcpPacket = TcpPacket::new_from_bytes_raw_payload( - &packet.payload, - IpAddr::V6(packet.source_address), - IpAddr::V6(packet.destination_address), - )?; - todo!() - } - _ => { - log::warn!("Unsupported next level protocol: {}", packet.next_header); - } - } - - Ok(()) -} diff --git a/src/nat/xlat/icmp.rs b/src/nat/xlat/icmp.rs index b683432..65e8cfd 100644 --- a/src/nat/xlat/icmp.rs +++ b/src/nat/xlat/icmp.rs @@ -15,108 +15,108 @@ use crate::{icmp_packet, icmpv6_packet, ipv4_packet, ipv6_packet}; use super::PacketTranslationError; -/// 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, -) -> Option<(Icmpv6Type, Icmpv6Code)> { - match (icmp_type, icmp_code) { - // Echo Request - (IcmpTypes::EchoRequest, _) => Some((Icmpv6Types::EchoRequest, Icmpv6Code(0))), +// /// 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, +// ) -> Option<(Icmpv6Type, Icmpv6Code)> { +// match (icmp_type, icmp_code) { +// // Echo Request +// (IcmpTypes::EchoRequest, _) => Some((Icmpv6Types::EchoRequest, Icmpv6Code(0))), - // Echo Reply - (IcmpTypes::EchoReply, _) => Some((Icmpv6Types::EchoReply, Icmpv6Code(0))), +// // Echo Reply +// (IcmpTypes::EchoReply, _) => Some((Icmpv6Types::EchoReply, Icmpv6Code(0))), - // Packet Too Big - ( - IcmpTypes::DestinationUnreachable, - destination_unreachable::IcmpCodes::FragmentationRequiredAndDFFlagSet, - ) => Some((Icmpv6Types::PacketTooBig, Icmpv6Code(0))), +// // 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, +// // 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, - }), - )), +// // Default to No Route to Destination +// _ => 0, +// }), +// )), - // Time Exceeded - (IcmpTypes::TimeExceeded, icmp_code) => { - Some((Icmpv6Types::TimeExceeded, Icmpv6Code(icmp_code.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 - } - } -} +// // Default unsupported +// _ => { +// log::warn!( +// "Unsupported ICMP code and type: {:?}, {:?}", +// icmp_type, +// icmp_code +// ); +// None +// } +// } +// } -/// 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, -) -> Option<(IcmpType, IcmpCode)> { - match (icmp_type, icmp_code) { - // Echo Request - (Icmpv6Types::EchoRequest, _) => Some((IcmpTypes::EchoRequest, IcmpCode(0))), +// /// 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, +// ) -> Option<(IcmpType, IcmpCode)> { +// match (icmp_type, icmp_code) { +// // Echo Request +// (Icmpv6Types::EchoRequest, _) => Some((IcmpTypes::EchoRequest, IcmpCode(0))), - // Echo Reply - (Icmpv6Types::EchoReply, _) => Some((IcmpTypes::EchoReply, IcmpCode(0))), +// // Echo Reply +// (Icmpv6Types::EchoReply, _) => Some((IcmpTypes::EchoReply, IcmpCode(0))), - // Packet Too Big - (Icmpv6Types::PacketTooBig, _) => Some(( - IcmpTypes::DestinationUnreachable, - destination_unreachable::IcmpCodes::FragmentationRequiredAndDFFlagSet, - )), +// // 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, - }, - )), +// // 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))) - } +// // 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 - } - } -} +// // Default unsupported +// _ => { +// log::warn!( +// "Unsupported ICMPv6 code and type: {:?}, {:?}", +// icmp_type, +// icmp_code +// ); +// None +// } +// } +// } /// Translate an ICMP packet into an ICMPv6 packet pub fn translate_icmp_4_to_6( diff --git a/src/nat/xlat/mod.rs b/src/nat/xlat/mod.rs index 1c928a3..ac64c72 100644 --- a/src/nat/xlat/mod.rs +++ b/src/nat/xlat/mod.rs @@ -1,17 +1,17 @@ -//! Packet type translation functionality +// //! Packet type translation functionality -mod icmp; -mod tcp; -mod udp; +// mod icmp; +// mod tcp; +// mod udp; -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}; +// 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}; -#[derive(Debug, thiserror::Error)] -pub enum PacketTranslationError { - #[error("Input packet too short. Got {0} bytes")] - InputPacketTooShort(usize), - #[error("Embedded packet too short. Got {0} bytes")] - EmbeddedPacketTooShort(usize), -} +// #[derive(Debug, thiserror::Error)] +// pub enum PacketTranslationError { +// #[error("Input packet too short. Got {0} bytes")] +// InputPacketTooShort(usize), +// #[error("Embedded packet too short. Got {0} bytes")] +// EmbeddedPacketTooShort(usize), +// } diff --git a/src/nat/xlat/tcp.rs b/src/nat/xlat/tcp.rs index 2ef4d1b..f8f3dd7 100644 --- a/src/nat/xlat/tcp.rs +++ b/src/nat/xlat/tcp.rs @@ -1,52 +1,52 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; +// use std::net::{Ipv4Addr, Ipv6Addr}; -use pnet_packet::{ - tcp::{self, MutableTcpPacket, TcpPacket}, - Packet, -}; +// use pnet_packet::{ +// tcp::{self, MutableTcpPacket, TcpPacket}, +// Packet, +// }; -use super::PacketTranslationError; +// use super::PacketTranslationError; -/// Translate an IPv4 TCP packet into an IPv6 TCP packet (aka: recalculate checksum) -pub fn translate_tcp_4_to_6( - ipv4_tcp: TcpPacket, - new_source: Ipv6Addr, - new_dest: Ipv6Addr, -) -> Result { - // Create a mutable clone of the IPv4 TCP packet, so it can be adapted for use in IPv6 - let mut ipv6_tcp = MutableTcpPacket::owned(ipv4_tcp.packet().to_vec()) - .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv4_tcp.packet().len()))?; +// /// Translate an IPv4 TCP packet into an IPv6 TCP packet (aka: recalculate checksum) +// pub fn translate_tcp_4_to_6( +// ipv4_tcp: TcpPacket, +// new_source: Ipv6Addr, +// new_dest: Ipv6Addr, +// ) -> Result { +// // Create a mutable clone of the IPv4 TCP packet, so it can be adapted for use in IPv6 +// let mut ipv6_tcp = MutableTcpPacket::owned(ipv4_tcp.packet().to_vec()) +// .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv4_tcp.packet().len()))?; - // Rewrite the checksum for use in an IPv6 packet - ipv6_tcp.set_checksum(0); - ipv6_tcp.set_checksum(tcp::ipv6_checksum( - &ipv4_tcp.to_immutable(), - &new_source, - &new_dest, - )); +// // Rewrite the checksum for use in an IPv6 packet +// ipv6_tcp.set_checksum(0); +// ipv6_tcp.set_checksum(tcp::ipv6_checksum( +// &ipv4_tcp.to_immutable(), +// &new_source, +// &new_dest, +// )); - // Return the translated packet - Ok(TcpPacket::owned(ipv6_tcp.packet().to_vec()).unwrap()) -} +// // Return the translated packet +// Ok(TcpPacket::owned(ipv6_tcp.packet().to_vec()).unwrap()) +// } -/// Translate an IPv6 TCP packet into an IPv4 TCP packet (aka: recalculate checksum) -pub fn translate_tcp_6_to_4( - ipv6_tcp: TcpPacket, - new_source: Ipv4Addr, - new_dest: Ipv4Addr, -) -> Result { - // Create a mutable clone of the IPv6 TCP packet, so it can be adapted for use in IPv4 - let mut ipv4_tcp = MutableTcpPacket::owned(ipv6_tcp.packet().to_vec()) - .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv6_tcp.packet().len()))?; +// /// Translate an IPv6 TCP packet into an IPv4 TCP packet (aka: recalculate checksum) +// pub fn translate_tcp_6_to_4( +// ipv6_tcp: TcpPacket, +// new_source: Ipv4Addr, +// new_dest: Ipv4Addr, +// ) -> Result { +// // Create a mutable clone of the IPv6 TCP packet, so it can be adapted for use in IPv4 +// let mut ipv4_tcp = MutableTcpPacket::owned(ipv6_tcp.packet().to_vec()) +// .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv6_tcp.packet().len()))?; - // Rewrite the checksum for use in an IPv4 packet - ipv4_tcp.set_checksum(0); - ipv4_tcp.set_checksum(tcp::ipv4_checksum( - &ipv6_tcp.to_immutable(), - &new_source, - &new_dest, - )); +// // Rewrite the checksum for use in an IPv4 packet +// ipv4_tcp.set_checksum(0); +// ipv4_tcp.set_checksum(tcp::ipv4_checksum( +// &ipv6_tcp.to_immutable(), +// &new_source, +// &new_dest, +// )); - // Return the translated packet - Ok(TcpPacket::owned(ipv4_tcp.packet().to_vec()).unwrap()) -} +// // Return the translated packet +// Ok(TcpPacket::owned(ipv4_tcp.packet().to_vec()).unwrap()) +// } diff --git a/src/nat/xlat/udp.rs b/src/nat/xlat/udp.rs index 51260dc..24e6bc5 100644 --- a/src/nat/xlat/udp.rs +++ b/src/nat/xlat/udp.rs @@ -1,117 +1,117 @@ -use super::PacketTranslationError; -use pnet_packet::{ - udp::{self, MutableUdpPacket, UdpPacket}, - Packet, -}; -use std::net::{Ipv4Addr, Ipv6Addr}; +// use super::PacketTranslationError; +// use pnet_packet::{ +// udp::{self, MutableUdpPacket, UdpPacket}, +// Packet, +// }; +// use std::net::{Ipv4Addr, Ipv6Addr}; -/// Translate an IPv4 UDP packet into an IPv6 UDP packet (aka: recalculate checksum) -pub fn translate_udp_4_to_6( - ipv4_udp: UdpPacket, - new_source: Ipv6Addr, - new_dest: Ipv6Addr, -) -> Result { - // Create a mutable clone of the IPv4 UDP packet, so it can be adapted for use in IPv6 - let mut ipv6_udp = MutableUdpPacket::owned(ipv4_udp.packet().to_vec()) - .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv4_udp.packet().len()))?; +// /// Translate an IPv4 UDP packet into an IPv6 UDP packet (aka: recalculate checksum) +// pub fn translate_udp_4_to_6( +// ipv4_udp: UdpPacket, +// new_source: Ipv6Addr, +// new_dest: Ipv6Addr, +// ) -> Result { +// // Create a mutable clone of the IPv4 UDP packet, so it can be adapted for use in IPv6 +// let mut ipv6_udp = MutableUdpPacket::owned(ipv4_udp.packet().to_vec()) +// .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv4_udp.packet().len()))?; - // Rewrite the checksum for use in an IPv6 packet - ipv6_udp.set_checksum(0); - ipv6_udp.set_checksum(udp::ipv6_checksum( - &ipv4_udp.to_immutable(), - &new_source, - &new_dest, - )); +// // Rewrite the checksum for use in an IPv6 packet +// ipv6_udp.set_checksum(0); +// ipv6_udp.set_checksum(udp::ipv6_checksum( +// &ipv4_udp.to_immutable(), +// &new_source, +// &new_dest, +// )); - // Return the translated packet - Ok(UdpPacket::owned(ipv6_udp.packet().to_vec()).unwrap()) -} +// // Return the translated packet +// Ok(UdpPacket::owned(ipv6_udp.packet().to_vec()).unwrap()) +// } -/// Translate an IPv6 UDP packet into an IPv4 UDP packet (aka: recalculate checksum) -pub fn translate_udp_6_to_4( - ipv6_udp: UdpPacket, - new_source: Ipv4Addr, - new_dest: Ipv4Addr, -) -> Result { - // Create a mutable clone of the IPv6 UDP packet, so it can be adapted for use in IPv4 - let mut ipv4_udp = MutableUdpPacket::owned(ipv6_udp.packet().to_vec()) - .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv6_udp.packet().len()))?; +// /// Translate an IPv6 UDP packet into an IPv4 UDP packet (aka: recalculate checksum) +// pub fn translate_udp_6_to_4( +// ipv6_udp: UdpPacket, +// new_source: Ipv4Addr, +// new_dest: Ipv4Addr, +// ) -> Result { +// // Create a mutable clone of the IPv6 UDP packet, so it can be adapted for use in IPv4 +// let mut ipv4_udp = MutableUdpPacket::owned(ipv6_udp.packet().to_vec()) +// .ok_or_else(|| PacketTranslationError::InputPacketTooShort(ipv6_udp.packet().len()))?; - // Rewrite the checksum for use in an IPv4 packet - ipv4_udp.set_checksum(0); - ipv4_udp.set_checksum(udp::ipv4_checksum( - &ipv6_udp.to_immutable(), - &new_source, - &new_dest, - )); +// // Rewrite the checksum for use in an IPv4 packet +// ipv4_udp.set_checksum(0); +// ipv4_udp.set_checksum(udp::ipv4_checksum( +// &ipv6_udp.to_immutable(), +// &new_source, +// &new_dest, +// )); - // Return the translated packet - Ok(UdpPacket::owned(ipv4_udp.packet().to_vec()).unwrap()) -} +// // Return the translated packet +// Ok(UdpPacket::owned(ipv4_udp.packet().to_vec()).unwrap()) +// } -#[cfg(test)] -mod tests { - use crate::into_udp; +// #[cfg(test)] +// mod tests { +// use crate::into_udp; - use super::*; +// use super::*; - #[test] - fn test_udp_4_to_6() { - // Build an example UDP packet - let input = into_udp!(vec![ - 0, 255, // Source port - 0, 128, // Destination port - 0, 4, // Length - 0, 0, // Checksum (doesn't matter) - 1, 2, 3, 4 // Data - ]) - .unwrap(); +// #[test] +// fn test_udp_4_to_6() { +// // Build an example UDP packet +// let input = into_udp!(vec![ +// 0, 255, // Source port +// 0, 128, // Destination port +// 0, 4, // Length +// 0, 0, // Checksum (doesn't matter) +// 1, 2, 3, 4 // Data +// ]) +// .unwrap(); - // Translate to IPv6 - let output = translate_udp_4_to_6( - input, - "2001:db8::1".parse().unwrap(), - "2001:db8::2".parse().unwrap(), - ); +// // Translate to IPv6 +// let output = translate_udp_4_to_6( +// input, +// "2001:db8::1".parse().unwrap(), +// "2001:db8::2".parse().unwrap(), +// ); - // Check the output - assert!(output.is_ok()); - let output = output.unwrap(); +// // Check the output +// assert!(output.is_ok()); +// let output = output.unwrap(); - // Check the output's contents - assert_eq!(output.get_source(), 255); - assert_eq!(output.get_destination(), 128); - assert_eq!(output.get_length(), 4); - assert_eq!(output.payload(), &[1, 2, 3, 4]); - } +// // Check the output's contents +// assert_eq!(output.get_source(), 255); +// assert_eq!(output.get_destination(), 128); +// assert_eq!(output.get_length(), 4); +// assert_eq!(output.payload(), &[1, 2, 3, 4]); +// } - #[test] - fn test_udp_6_to_4() { - // Build an example UDP packet - let input = into_udp!(vec![ - 0, 255, // Source port - 0, 128, // Destination port - 0, 4, // Length - 0, 0, // Checksum (doesn't matter) - 1, 2, 3, 4 // Data - ]) - .unwrap(); +// #[test] +// fn test_udp_6_to_4() { +// // Build an example UDP packet +// let input = into_udp!(vec![ +// 0, 255, // Source port +// 0, 128, // Destination port +// 0, 4, // Length +// 0, 0, // Checksum (doesn't matter) +// 1, 2, 3, 4 // Data +// ]) +// .unwrap(); - // Translate to IPv4 - let output = translate_udp_6_to_4( - input, - "192.0.2.1".parse().unwrap(), - "192.0.2.2".parse().unwrap(), - ); +// // Translate to IPv4 +// let output = translate_udp_6_to_4( +// input, +// "192.0.2.1".parse().unwrap(), +// "192.0.2.2".parse().unwrap(), +// ); - // Check the output - assert!(output.is_ok()); - let output = output.unwrap(); +// // Check the output +// assert!(output.is_ok()); +// let output = output.unwrap(); - // Check the output's contents - assert_eq!(output.get_source(), 255); - assert_eq!(output.get_destination(), 128); - assert_eq!(output.get_length(), 4); - assert_eq!(output.payload(), &[1, 2, 3, 4]); - } -} +// // Check the output's contents +// assert_eq!(output.get_source(), 255); +// assert_eq!(output.get_destination(), 128); +// assert_eq!(output.get_length(), 4); +// assert_eq!(output.payload(), &[1, 2, 3, 4]); +// } +// } diff --git a/src/packet/error.rs b/src/packet/error.rs index c99cf36..fc65fcd 100644 --- a/src/packet/error.rs +++ b/src/packet/error.rs @@ -6,5 +6,9 @@ pub enum PacketError { MismatchedAddressFamily(IpAddr, IpAddr), #[error("Packet too short: {0}")] TooShort(usize), + #[error("Unsupported ICMP type: {0}")] + UnsupportedIcmpType(u8), + #[error("Unsupported ICMPv6 type: {0}")] + UnsupportedIcmpv6Type(u8), } diff --git a/src/packet/mod.rs b/src/packet/mod.rs index b557b74..a40cbd5 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -4,3 +4,4 @@ pub mod error; pub mod protocols; +pub mod xlat; diff --git a/src/packet/protocols/raw.rs b/src/packet/protocols/raw.rs index 95bca85..b3bbb3a 100644 --- a/src/packet/protocols/raw.rs +++ b/src/packet/protocols/raw.rs @@ -10,3 +10,9 @@ impl TryFrom> for RawBytes { Ok(Self(bytes)) } } + +impl Into> for RawBytes { + fn into(self) -> Vec { + self.0 + } +} \ No newline at end of file diff --git a/src/packet/protocols/tcp.rs b/src/packet/protocols/tcp.rs index 97f94d1..f74f323 100644 --- a/src/packet/protocols/tcp.rs +++ b/src/packet/protocols/tcp.rs @@ -182,15 +182,18 @@ impl TcpPacket { impl Into> for TcpPacket where - T: Into> + Copy, + T: Into> , { fn into(self) -> Vec { + // Get the options length in words + let options_length_words = self.options_length_words(); + // Convert the payload into raw bytes let payload: Vec = self.payload.into(); // Allocate a mutable packet to write into let total_length = pnet_packet::tcp::MutableTcpPacket::minimum_packet_size() - + (self.options_length_words() as usize * 4) + + (options_length_words as usize * 4) + payload.len(); let mut output = pnet_packet::tcp::MutableTcpPacket::owned(vec![0u8; total_length]).unwrap(); @@ -207,7 +210,7 @@ where output.set_options(&self.options); // Write the offset - output.set_data_offset(5 + self.options_length_words()); + output.set_data_offset(5 + options_length_words); // Write the flags output.set_flags(self.flags.into()); diff --git a/src/packet/xlat/icmp/mod.rs b/src/packet/xlat/icmp/mod.rs new file mode 100644 index 0000000..967cd68 --- /dev/null +++ b/src/packet/xlat/icmp/mod.rs @@ -0,0 +1,92 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + +use pnet_packet::{icmp::IcmpTypes, icmpv6::Icmpv6Types}; + +use crate::packet::{ + error::PacketError, + protocols::{icmp::IcmpPacket, icmpv6::Icmpv6Packet, raw::RawBytes}, +}; + +use super::ip::{translate_ipv4_to_ipv6, translate_ipv6_to_ipv4}; + +mod type_code; + +/// Translates an ICMP packet to an ICMPv6 packet +pub fn translate_icmp_to_icmpv6( + input: IcmpPacket, + new_source: Ipv6Addr, + new_destination: Ipv6Addr, +) -> Result, PacketError> { + // Translate the type and code + let (icmpv6_type, icmpv6_code) = + type_code::translate_type_and_code_4_to_6(input.icmp_type, input.icmp_code)?; + + // Some ICMP types require special payload edits + let payload = match icmpv6_type { + Icmpv6Types::TimeExceeded => { + // In this case, the current payload looks like: 4bytes + Ipv4(Data) + // This needs to be translated to: 4bytes + Ipv6(Data) + let inner_payload = input.payload.0[4..].to_vec(); + + // Translate + let inner_payload = + translate_ipv4_to_ipv6(inner_payload.try_into()?, new_source, new_destination)?; + let inner_payload: Vec = inner_payload.into(); + + // Build the new payload + RawBytes({ + let mut buffer = Vec::with_capacity(4 + inner_payload.len()); + buffer.extend_from_slice(&input.payload.0[..4]); + buffer.extend_from_slice(&inner_payload); + buffer + }) + } + _ => input.payload, + }; + + // Build output packet + Ok(Icmpv6Packet::new( + new_source, + new_destination, + icmpv6_type, + icmpv6_code, + payload, + )) +} + +/// Translates an ICMPv6 packet to an ICMP packet +pub fn translate_icmpv6_to_icmp( + input: Icmpv6Packet, + new_source: Ipv4Addr, + new_destination: Ipv4Addr, +) -> Result, PacketError> { + // Translate the type and code + let (icmp_type, icmp_code) = + type_code::translate_type_and_code_6_to_4(input.icmp_type, input.icmp_code)?; + + // Some ICMP types require special payload edits + let payload = match icmp_type { + IcmpTypes::TimeExceeded => { + // In this case, the current payload looks like: 4bytes + Ipv6(Data) + // This needs to be translated to: 4bytes + Ipv4(Data) + let inner_payload = input.payload.0[4..].to_vec(); + + // Translate + let inner_payload = + translate_ipv6_to_ipv4(inner_payload.try_into()?, new_source, new_destination)?; + let inner_payload: Vec = inner_payload.into(); + + // Build the new payload + RawBytes({ + let mut buffer = Vec::with_capacity(4 + inner_payload.len()); + buffer.extend_from_slice(&input.payload.0[..4]); + buffer.extend_from_slice(&inner_payload); + buffer + }) + }, + _ => input.payload, + }; + + // Build output packet + Ok(IcmpPacket::new(icmp_type, icmp_code, payload)) +} diff --git a/src/packet/xlat/icmp/type_code.rs b/src/packet/xlat/icmp/type_code.rs new file mode 100644 index 0000000..e7cb6cd --- /dev/null +++ b/src/packet/xlat/icmp/type_code.rs @@ -0,0 +1,97 @@ +//! Functions to map between ICMP and ICMPv6 types/codes + +use pnet_packet::{ + icmp::{destination_unreachable, IcmpCode, IcmpType, IcmpTypes}, + icmpv6::{Icmpv6Code, Icmpv6Type, Icmpv6Types}, +}; + +use crate::packet::error::PacketError; + +/// Best effort translation from an ICMP type and code to an ICMPv6 type and code +pub fn translate_type_and_code_4_to_6( + icmp_type: IcmpType, + icmp_code: IcmpCode, +) -> Result<(Icmpv6Type, Icmpv6Code), PacketError> { + match (icmp_type, icmp_code) { + // Echo Request + (IcmpTypes::EchoRequest, _) => Ok((Icmpv6Types::EchoRequest, Icmpv6Code(0))), + + // Echo Reply + (IcmpTypes::EchoReply, _) => Ok((Icmpv6Types::EchoReply, Icmpv6Code(0))), + + // Packet Too Big + ( + IcmpTypes::DestinationUnreachable, + destination_unreachable::IcmpCodes::FragmentationRequiredAndDFFlagSet, + ) => Ok((Icmpv6Types::PacketTooBig, Icmpv6Code(0))), + + // Destination Unreachable + (IcmpTypes::DestinationUnreachable, icmp_code) => Ok(( + 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) => { + Ok((Icmpv6Types::TimeExceeded, Icmpv6Code(icmp_code.0))) + } + + // Default unsupported + (icmp_type, _) => Err(PacketError::UnsupportedIcmpType(icmp_type.0)), + } +} + +/// Best effort translation from an ICMPv6 type and code to an ICMP type and code +pub fn translate_type_and_code_6_to_4( + icmp_type: Icmpv6Type, + icmp_code: Icmpv6Code, +) -> Result<(IcmpType, IcmpCode), PacketError> { + match (icmp_type, icmp_code) { + // Echo Request + (Icmpv6Types::EchoRequest, _) => Ok((IcmpTypes::EchoRequest, IcmpCode(0))), + + // Echo Reply + (Icmpv6Types::EchoReply, _) => Ok((IcmpTypes::EchoReply, IcmpCode(0))), + + // Packet Too Big + (Icmpv6Types::PacketTooBig, _) => Ok(( + IcmpTypes::DestinationUnreachable, + destination_unreachable::IcmpCodes::FragmentationRequiredAndDFFlagSet, + )), + + // Destination Unreachable + (Icmpv6Types::DestinationUnreachable, icmp_code) => Ok(( + 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) => { + Ok((IcmpTypes::TimeExceeded, IcmpCode(icmp_code.0))) + } + + // Default unsupported + (icmp_type, _) => Err(PacketError::UnsupportedIcmpv6Type(icmp_type.0)), + } +} diff --git a/src/packet/xlat/ip.rs b/src/packet/xlat/ip.rs new file mode 100644 index 0000000..540638c --- /dev/null +++ b/src/packet/xlat/ip.rs @@ -0,0 +1,129 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use pnet_packet::ip::IpNextHeaderProtocols; + +use crate::{ + packet::protocols::{icmp::IcmpPacket, tcp::TcpPacket, udp::UdpPacket}, + packet::{ + error::PacketError, + protocols::{icmpv6::Icmpv6Packet, ipv4::Ipv4Packet, ipv6::Ipv6Packet, raw::RawBytes}, + }, +}; + +use super::{ + icmp::{translate_icmp_to_icmpv6, translate_icmpv6_to_icmp}, + tcp::{translate_tcp4_to_tcp6, translate_tcp6_to_tcp4}, + udp::{translate_udp4_to_udp6, translate_udp6_to_udp4}, +}; + +/// Translates an IPv4 packet to an IPv6 packet +pub fn translate_ipv4_to_ipv6( + input: Ipv4Packet>, + new_source: Ipv6Addr, + new_destination: Ipv6Addr, +) -> Result>, PacketError> { + // Perform recursive translation to determine the new payload + let new_payload = match input.protocol { + IpNextHeaderProtocols::Icmp => { + let icmp_input: IcmpPacket = input.payload.try_into()?; + translate_icmp_to_icmpv6(icmp_input, new_source, new_destination)?.into() + } + IpNextHeaderProtocols::Udp => { + let udp_input: UdpPacket = UdpPacket::new_from_bytes_raw_payload( + &input.payload, + IpAddr::V4(input.source_address), + IpAddr::V4(input.destination_address), + )?; + translate_udp4_to_udp6(udp_input, new_source, new_destination)?.into() + } + IpNextHeaderProtocols::Tcp => { + let tcp_input: TcpPacket = TcpPacket::new_from_bytes_raw_payload( + &input.payload, + IpAddr::V4(input.source_address), + IpAddr::V4(input.destination_address), + )?; + translate_tcp4_to_tcp6(tcp_input, new_source, new_destination)?.into() + } + _ => { + log::warn!("Unsupported next level protocol: {}", input.protocol); + input.payload + } + }; + + // Build the output IPv6 packet + let output = Ipv6Packet::new( + 0, + 0, + match input.protocol { + IpNextHeaderProtocols::Icmp => IpNextHeaderProtocols::Icmpv6, + proto => proto, + }, + input.ttl, + new_source, + new_destination, + new_payload, + ); + + // Return the output + Ok(output) +} + +/// Translates an IPv6 packet to an IPv4 packet +pub fn translate_ipv6_to_ipv4( + input: Ipv6Packet>, + new_source: Ipv4Addr, + new_destination: Ipv4Addr, +) -> Result>, PacketError> { + // Perform recursive translation to determine the new payload + let new_payload = match input.next_header { + IpNextHeaderProtocols::Icmpv6 => { + let icmpv6_input: Icmpv6Packet = Icmpv6Packet::new_from_bytes_raw_payload( + &input.payload, + input.source_address, + input.destination_address, + )?; + Some(translate_icmpv6_to_icmp(icmpv6_input, new_source, new_destination)?.into()) + } + IpNextHeaderProtocols::Udp => { + let udp_input: UdpPacket = UdpPacket::new_from_bytes_raw_payload( + &input.payload, + IpAddr::V6(input.source_address), + IpAddr::V6(input.destination_address), + )?; + Some(translate_udp6_to_udp4(udp_input, new_source, new_destination)?.into()) + } + IpNextHeaderProtocols::Tcp => { + let tcp_input: TcpPacket = TcpPacket::new_from_bytes_raw_payload( + &input.payload, + IpAddr::V6(input.source_address), + IpAddr::V6(input.destination_address), + )?; + Some(translate_tcp6_to_tcp4(tcp_input, new_source, new_destination)?.into()) + } + _ => { + log::warn!("Unsupported next level protocol: {}", input.next_header); + None + } + }; + + // Build the output IPv4 packet + let output = Ipv4Packet::new( + 0, + 0, + 0, + 0, + 0, + input.hop_limit, + match input.next_header { + IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp, + proto => proto, + }, + new_source, + new_destination, + vec![], + new_payload.unwrap_or_else(Vec::new), + ); + + // Return the output + Ok(output) +} diff --git a/src/packet/xlat/mod.rs b/src/packet/xlat/mod.rs new file mode 100644 index 0000000..0c842c1 --- /dev/null +++ b/src/packet/xlat/mod.rs @@ -0,0 +1,6 @@ +//! Protocol translation logic + +pub mod icmp; +pub mod ip; +pub mod tcp; +pub mod udp; diff --git a/src/packet/xlat/tcp.rs b/src/packet/xlat/tcp.rs new file mode 100644 index 0000000..bff8619 --- /dev/null +++ b/src/packet/xlat/tcp.rs @@ -0,0 +1,46 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + +use crate::packet::{ + error::PacketError, + protocols::{raw::RawBytes, tcp::TcpPacket}, +}; + +/// Translates an IPv4 TCP packet to an IPv6 TCP packet +pub fn translate_tcp4_to_tcp6( + input: TcpPacket, + new_source_addr: Ipv6Addr, + new_destination_addr: Ipv6Addr, +) -> Result, PacketError> { + // Build the packet + Ok(TcpPacket::new( + SocketAddr::new(IpAddr::V6(new_source_addr), input.source().port()), + SocketAddr::new(IpAddr::V6(new_destination_addr), input.destination().port()), + input.sequence, + input.ack_number, + input.flags, + input.window_size, + input.urgent_pointer, + input.options, + input.payload, + )?) +} + +/// Translates an IPv6 TCP packet to an IPv4 TCP packet +pub fn translate_tcp6_to_tcp4( + input: TcpPacket, + new_source_addr: Ipv4Addr, + new_destination_addr: Ipv4Addr, +) -> Result, PacketError> { + // Build the packet + Ok(TcpPacket::new( + SocketAddr::new(IpAddr::V4(new_source_addr), input.source().port()), + SocketAddr::new(IpAddr::V4(new_destination_addr), input.destination().port()), + input.sequence, + input.ack_number, + input.flags, + input.window_size, + input.urgent_pointer, + input.options, + input.payload, + )?) +} diff --git a/src/packet/xlat/udp.rs b/src/packet/xlat/udp.rs new file mode 100644 index 0000000..4159f0b --- /dev/null +++ b/src/packet/xlat/udp.rs @@ -0,0 +1,34 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + +use crate::packet::{ + error::PacketError, + protocols::{raw::RawBytes, udp::UdpPacket}, +}; + +/// Translates an IPv4 UDP packet to an IPv6 UDP packet +pub fn translate_udp4_to_udp6( + input: UdpPacket, + new_source_addr: Ipv6Addr, + new_destination_addr: Ipv6Addr, +) -> Result, PacketError> { + // Build the packet + Ok(UdpPacket::new( + SocketAddr::new(IpAddr::V6(new_source_addr), input.source().port()), + SocketAddr::new(IpAddr::V6(new_destination_addr), input.destination().port()), + input.payload, + )?) +} + +/// Translates an IPv6 UDP packet to an IPv4 UDP packet +pub fn translate_udp6_to_udp4( + input: UdpPacket, + new_source_addr: Ipv4Addr, + new_destination_addr: Ipv4Addr, +) -> Result, PacketError> { + // Build the packet + Ok(UdpPacket::new( + SocketAddr::new(IpAddr::V4(new_source_addr), input.source().port()), + SocketAddr::new(IpAddr::V4(new_destination_addr), input.destination().port()), + input.payload, + )?) +}