From 37eea69943d456633725a275c516707d0065d47d Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 16 Jul 2023 22:18:36 -0400 Subject: [PATCH] Starting the big refactor (cleaned up udp) --- .vscode/settings.json | 2 + src/main.rs | 1 - src/nat/macros.rs | 103 ++++++++++++ src/nat/mod.rs | 100 ++++++++--- src/nat/mod.rs.old | 376 ------------------------------------------ src/nat/xlat/mod.rs | 11 +- src/nat/xlat/udp.rs | 231 ++++++++++++++++---------- src/types.rs | 33 ---- 8 files changed, 328 insertions(+), 529 deletions(-) create mode 100644 src/nat/macros.rs delete mode 100644 src/nat/mod.rs.old delete mode 100644 src/types.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 0eef518..b7f64a4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { "cSpell.words": [ + "Datagram", + "pnet", "rtnetlink" ] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7060eb3..13b3a8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use nat::Nat64; mod cli; mod config; mod nat; -mod types; #[tokio::main] pub async fn main() { diff --git a/src/nat/macros.rs b/src/nat/macros.rs new file mode 100644 index 0000000..ecac203 --- /dev/null +++ b/src/nat/macros.rs @@ -0,0 +1,103 @@ +/// 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 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() + }}; +} + +/// 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) => { + 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() + }}; +} diff --git a/src/nat/mod.rs b/src/nat/mod.rs index c83a565..4e27a90 100644 --- a/src/nat/mod.rs +++ b/src/nat/mod.rs @@ -4,15 +4,18 @@ use std::{ }; use ipnet::{Ipv4Net, Ipv6Net}; -use pnet_packet::ip::IpNextHeaderProtocols; +use pnet_packet::{ip::IpNextHeaderProtocols, Packet}; + +use crate::{into_udp, ipv4_packet, ipv6_packet, nat::xlat::translate_udp_4_to_6}; use self::{ interface::Nat64Interface, - packet::{IpPacket, PacketError}, + packet::IpPacket, table::{Nat64Table, TableError}, }; mod interface; +mod macros; mod packet; mod table; mod xlat; @@ -26,11 +29,7 @@ pub enum Nat64Error { #[error(transparent)] IoError(#[from] std::io::Error), #[error(transparent)] - UdpProxyError(#[from] xlat::UdpProxyError), - #[error(transparent)] - IcmpProxyError(#[from] xlat::IcmpProxyError), - #[error(transparent)] - TcpProxyError(#[from] xlat::TcpProxyError), + XlatError(#[from] xlat::PacketTranslationError), } pub struct Nat64 { @@ -81,7 +80,10 @@ impl Nat64 { // If data is returned, send it back out the interface Some(outbound_packet) => { let packet_bytes = outbound_packet.to_bytes(); - log::debug!("Outbound packet next header: {}", outbound_packet.get_next_header().0); + log::debug!( + "Outbound packet next header: {}", + outbound_packet.get_next_header().0 + ); log::debug!("Sending packet: {:?}", packet_bytes); self.interface.send(&packet_bytes).unwrap(); } @@ -152,25 +154,71 @@ impl Nat64 { ); // Different logic is required for ICMP, UDP, and TCP - let next_header_protocol = packet.get_next_header(); - log::debug!( - "Incoming packet has next header protocol: {}", - next_header_protocol - ); - 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) + match (packet, new_source, new_destination) { + (IpPacket::V4(packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => { + match packet.get_next_level_protocol() { + // User Datagram Protocol + IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V6(ipv6_packet!( + new_source, + new_destination, + IpNextHeaderProtocols::Udp, + packet.get_ttl(), + translate_udp_4_to_6( + into_udp!(packet.payload().to_vec())?, + new_source, + new_destination + )? + .packet() + )))), + + // For any protocol we don't support, just warn and drop the packet + next_level_protocol => { + log::warn!("Unsupported next level protocol: {}", next_level_protocol); + Ok(None) + } + } } + (IpPacket::V6(packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => { + match packet.get_next_header() { + // User Datagram Protocol + IpNextHeaderProtocols::Udp => Ok(Some(IpPacket::V4(ipv4_packet!( + new_source, + new_destination, + packet.get_hop_limit(), + IpNextHeaderProtocols::Udp, + xlat::translate_udp_6_to_4( + into_udp!(packet.payload().to_vec())?, + new_source, + new_destination + )? + .packet() + )))), + + // For any protocol we don't support, just warn and drop the packet + next_header_protocol => { + log::warn!("Unsupported next header protocol: {}", next_header_protocol); + Ok(None) + } + } + } + + // 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/mod.rs.old b/src/nat/mod.rs.old deleted file mode 100644 index c31b46a..0000000 --- a/src/nat/mod.rs.old +++ /dev/null @@ -1,376 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -use bimap::BiMap; -use colored::Colorize; -use ipnet::{Ipv4Net, Ipv6Net}; -use pnet_packet::{ - icmp::IcmpPacket, - icmpv6::Icmpv6Packet, - ip::IpNextHeaderProtocols, - ipv4::{self, Ipv4Packet, MutableIpv4Packet}, - ipv6::{Ipv6Packet, MutableIpv6Packet}, - Packet, -}; -use tokio::process::Command; -use tun_tap::{Iface, Mode}; - -use crate::nat::packet::IpPacket; - -mod xlat; -mod packet; -mod table; - -/// A cleaner way to execute a CLI command -macro_rules! command { - ($cmd:expr, $($arg:expr),*) => {{ - Command::new($cmd) - $(.arg($arg))* - .status() - }} -} - -/// Converts bytes to a hex string for debugging -fn bytes_to_hex_str(bytes: &[u8]) -> String { - bytes - .iter() - .map(|val| format!("{:02x}", val)) - .collect::>() - .join(" ") -} - -pub struct Nat64 { - /// Handle for the Tun interface - interface: Iface, - /// IPv4 pool - ipv4_pool: Vec, - /// IPv6 prefix - ipv6_prefix: Ipv6Net, - /// A mapping of currently allocated pool reservations - pool_reservations: BiMap, -} - -impl Nat64 { - /// Bring up a new NAT64 interface - /// - /// **Arguments:** - /// - `ipv4_pool`: A list of IPv4 prefixes to communicate from - /// - `ipv6_prefix`: The IPv6 prefix to listen on (should generally be `64:ff9b::/96`) - pub async fn new( - ipv4_pool: Vec, - ipv6_prefix: Ipv6Net, - static_mappings: Vec<(Ipv4Addr, Ipv6Addr)>, - ) -> Result { - // Bring up tun interface - let interface = Iface::without_packet_info("nat64i%d", Mode::Tun)?; - - // Configure the interface - let interface_name = interface.name(); - log::info!("Configuring interface {}", interface_name); - - #[cfg_attr(rustfmt, rustfmt_skip)] - { - // Add the nat addresses - log::debug!("Assigning {} to {}", nat_v4, interface_name); - command!("ip", "address", "add", format!("{}/32", nat_v4), "dev", interface_name).await?; - log::debug!("Assigning {} to {}", nat_v6, interface_name); - command!("ip", "address", "add", format!("{}/128", nat_v6), "dev", interface_name ).await?; - - // Bring up the interface - log::debug!("Bringing up {}", interface_name); - command!("ip", "link", "set", "dev", interface_name, "up").await?; - - // Add route for IPv6 prefix - log::debug!("Adding route {} via {}", ipv6_prefix, interface_name); - command!("ip", "route", "add", ipv6_prefix.to_string(), "dev", interface_name).await?; - - // Configure iptables - log::debug!("Configuring iptables"); - command!("iptables", "-A", "FORWARD", "-i", interface_name, "-j", "ACCEPT").await?; - command!("iptables", "-A", "FORWARD", "-o", interface_name, "-j", "ACCEPT").await?; - command!("ip6tables", "-A", "FORWARD", "-i", interface_name, "-j", "ACCEPT").await?; - command!("ip6tables", "-A", "FORWARD", "-o", interface_name, "-j", "ACCEPT").await?; - } - - // Add every IPv4 prefix to the routing table - for prefix in ipv4_pool.iter() { - log::debug!("Adding route {} via {}", prefix, interface_name); - command!( - "ip", - "route", - "add", - prefix.to_string(), - "dev", - interface_name - ) - .await?; - } - - // Build a reservation list - let mut pool_reservations = BiMap::new(); - for (v4, v6) in static_mappings { - pool_reservations.insert(v4, v6); - } - pool_reservations.insert(nat_v4, nat_v6); - - Ok(Self { - interface, - ipv4_pool, - ipv6_prefix, - pool_reservations, - }) - } - - /// Block and run the NAT instance. This will handle all packets - pub async fn run(&mut self) -> Result<(), std::io::Error> { - // Read the interface MTU - let mtu: u16 = - std::fs::read_to_string(format!("/sys/class/net/{}/mtu", self.interface.name())) - .expect("Failed to read interface MTU") - .strip_suffix("\n") - .unwrap() - .parse() - .unwrap(); - - // Allocate a buffer for incoming packets - let mut buffer = vec![0; mtu as usize]; - - log::info!("Translating packets"); - loop { - // Read incoming packet - let len = self.interface.recv(&mut buffer)?; - - // Process the packet - let response = self.process(&buffer[..len]).await?; - - // If there is a response, send it - if let Some(response) = response { - self.interface.send(&response)?; - } - } - } - - /// Internal function that checks if a destination address is allowed to be processed - fn is_dest_allowed(&self, dest: IpAddr) -> bool { - return dest == self.instance_v4 - || dest == self.instance_v6 - || match dest { - IpAddr::V4(addr) => self.ipv4_pool.iter().any(|prefix| prefix.contains(&addr)), - IpAddr::V6(addr) => self.ipv6_prefix.contains(&addr), - }; - } - - /// Calculate a unique IPv4 address inside the pool for a given IPv6 address - fn calculate_ipv4(&self, _addr: Ipv6Addr) -> Option { - // Search the list of possible IPv4 addresses - for prefix in self.ipv4_pool.iter() { - for addr in prefix.hosts() { - // If this address is available, use it - if !self.pool_reservations.contains_left(&addr) { - return Some(addr); - } - } - } - - None - } - - /// Embeds an IPv4 address into an IPv6 address - fn embed_v4_into_v6(&self, addr: Ipv4Addr) -> Ipv6Addr { - let mut octets = [0u8; 16]; - octets[..12].copy_from_slice(&self.ipv6_prefix.network().octets()[..12]); - octets[12..].copy_from_slice(&addr.octets()); - Ipv6Addr::from(octets) - } - - /// Extracts an IPv4 address from an IPv6 address - fn extract_v4_from_v6(&self, addr: Ipv6Addr) -> Ipv4Addr { - let mut octets = [0u8; 4]; - octets.copy_from_slice(&addr.octets()[12..]); - Ipv4Addr::from(octets) - } - - /// Gets or creates a reservation for a given address - fn get_or_create_reservation(&mut self, addr: IpAddr) -> Option { - match addr { - IpAddr::V4(addr) => { - if self.pool_reservations.contains_left(&addr) { - return Some(IpAddr::V6( - *self.pool_reservations.get_by_left(&addr).unwrap(), - )); - } else { - return None; - } - } - IpAddr::V6(addr) => { - // If the address is already reserved, return it - if self.pool_reservations.contains_right(&addr) { - return Some(IpAddr::V4( - *self.pool_reservations.get_by_right(&addr).unwrap(), - )); - } - - // Otherwise, calculate a new address - let new_addr = self.calculate_ipv4(addr)?; - self.pool_reservations.insert(new_addr, addr); - return Some(IpAddr::V4(new_addr)); - } - } - } - - /// Internal function to process an incoming packet. - /// If `Some` is returned, the result is sent back out the interface - async fn process(&mut self, packet: &[u8]) -> Result>, std::io::Error> { - // Parse the packet - let input_packet = IpPacket::new(&packet); - if let None = input_packet { - log::warn!( - "{}", - format!( - "Malformed packet received: version: {}, len: {}", - packet[0] >> 4, - packet.len() - ) - .yellow() - ); - return Ok(None); - } - let input_packet = input_packet.unwrap(); - - // Log some info about the packet - #[cfg_attr(rustfmt, rustfmt_skip)] - { - log::debug!("Processing packet with length: {}", input_packet.len().to_string().bright_cyan()); - log::debug!("> IP Header: {}", bytes_to_hex_str(input_packet.get_header()).bright_cyan()); - log::debug!("> Source: {}", input_packet.get_source().to_string().bright_cyan()); - log::debug!("> Destination: {}", input_packet.get_destination().to_string().bright_cyan()); - log::debug!("> Next Header: {}", input_packet.get_next_header().to_string().bright_cyan()); - } - - // Ignore packets that aren't destined for the NAT instance - if !self.is_dest_allowed(input_packet.get_destination()) { - log::debug!("{}", "Ignoring packet. Invalid destination".yellow()); - return Ok(None); - } - - // Drop packets with 0 TTL - if input_packet.get_ttl() == 0 { - log::debug!("{}", "Ignoring packet. TTL is 0".yellow()); - return Ok(None); - } - - // Handle packet translation - let output_packet = match input_packet { - IpPacket::V4(packet) => { - let new_source = self.embed_v4_into_v6(packet.get_source()); - let new_dest = - self.get_or_create_reservation(std::net::IpAddr::V4(packet.get_destination())); - if let Some(IpAddr::V6(new_dest)) = new_dest { - // Log the new addresses - #[cfg_attr(rustfmt, rustfmt_skip)] - { - log::debug!("> Mapped IPv6 Source: {}", new_source.to_string().bright_cyan()); - log::debug!("> Mapped IPv6 Destination: {}", new_dest.to_string().bright_cyan()); - } - - // Handle inner packet conversion for protocols that don't support both v4 and v6 - if let Some(packet) = - Ipv4Packet::owned(match packet.get_next_level_protocol() { - // ICMP must be translated to ICMPv6 - IpNextHeaderProtocols::Icmp => { - if let Some(new_payload) = xlat::icmp_to_icmpv6( - &IcmpPacket::new(packet.payload()).unwrap(), - &new_source, - &new_dest, - ) { - // Mutate the input packet - let mut packet = - MutableIpv4Packet::owned(packet.packet().to_vec()).unwrap(); - packet.set_next_level_protocol(IpNextHeaderProtocols::Icmpv6); - packet.set_payload(&new_payload.packet().to_vec()); - packet.set_checksum(ipv4::checksum(&packet.to_immutable())); - packet.packet().to_vec() - } else { - return Ok(None); - } - } - - // By default, packets can be directly fed to the next function - _ => packet.packet().to_vec(), - }) - { - // Translate the packet - let translated = xlat::ipv4_to_ipv6(&packet, new_source, new_dest, true); - - // Log the translated packet header - log::debug!( - "> Translated Header: {}", - bytes_to_hex_str(&translated[0..40]).bright_cyan() - ); - - // Return the translated packet - translated - } else { - return Ok(None); - } - } else { - return Ok(None); - } - } - IpPacket::V6(packet) => { - let new_source = - self.get_or_create_reservation(std::net::IpAddr::V6(packet.get_source())); - let new_dest = self.extract_v4_from_v6(packet.get_destination()); - if let Some(IpAddr::V4(new_source)) = new_source { - // Log the new addresses - #[cfg_attr(rustfmt, rustfmt_skip)] - { - log::debug!("> Mapped IPv4 Source: {}", new_source.to_string().bright_cyan()); - log::debug!("> Mapped IPv4 Destination: {}", new_dest.to_string().bright_cyan()); - } - - // Handle inner packet conversion for protocols that don't support both v4 and v6 - if let Some(packet) = Ipv6Packet::owned(match packet.get_next_header() { - // ICMPv6 must be translated to ICMP - IpNextHeaderProtocols::Icmpv6 => { - if let Some(new_payload) = - xlat::icmpv6_to_icmp(&Icmpv6Packet::new(packet.payload()).unwrap()) - { - // Mutate the input packet - let mut packet = - MutableIpv6Packet::owned(packet.packet().to_vec()).unwrap(); - packet.set_next_header(IpNextHeaderProtocols::Icmp); - packet.set_payload(&new_payload.packet().to_vec()); - packet.packet().to_vec() - } else { - return Ok(None); - } - } - - // By default, packets can be directly fed to the next function - _ => packet.packet().to_vec(), - }) { - // Translate the packet - let translated = xlat::ipv6_to_ipv4(&packet, new_source, new_dest, true); - - // Log the translated packet header - log::debug!( - "> Translated Header: {}", - bytes_to_hex_str(&translated[0..20]).bright_cyan() - ); - - // Return the translated packet - translated - } else { - return Ok(None); - } - } else { - return Ok(None); - } - } - }; - - // Build the response - log::debug!("{}", "Sending translated packet".bright_green()); - return Ok(Some(output_packet)); - } -} diff --git a/src/nat/xlat/mod.rs b/src/nat/xlat/mod.rs index c09fd54..7f6af6f 100644 --- a/src/nat/xlat/mod.rs +++ b/src/nat/xlat/mod.rs @@ -1,11 +1,16 @@ //! Packet type translation functionality mod icmp; -mod ip; mod tcp; mod udp; pub use icmp::{proxy_icmp_packet, IcmpProxyError}; -pub use ip::{ipv4_to_ipv6, ipv6_to_ipv4}; pub use tcp::{proxy_tcp_packet, TcpProxyError}; -pub use udp::{proxy_udp_packet, UdpProxyError}; +// pub use udp::{proxy_udp_packet, UdpProxyError}; +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), +} diff --git a/src/nat/xlat/udp.rs b/src/nat/xlat/udp.rs index de9171a..a0d1564 100644 --- a/src/nat/xlat/udp.rs +++ b/src/nat/xlat/udp.rs @@ -1,102 +1,153 @@ -use std::net::IpAddr; +use std::net::{Ipv4Addr, Ipv6Addr}; use pnet_packet::{ - ip::IpNextHeaderProtocols, - ipv4::{self, Ipv4Packet, MutableIpv4Packet}, - ipv6::{Ipv6Packet, MutableIpv6Packet}, + // ip::IpNextHeaderProtocols, + // ipv4::{self, Ipv4Packet, MutableIpv4Packet}, + // ipv6::{Ipv6Packet, MutableIpv6Packet}, udp::{self, MutableUdpPacket, UdpPacket}, Packet, }; -use crate::nat::packet::IpPacket; +// use crate::nat::packet::IpPacket; -#[derive(Debug, thiserror::Error)] -pub enum UdpProxyError { - #[error("Packet too short. Got {0} bytes")] - PacketTooShort(usize), +use super::PacketTranslationError; + +// #[derive(Debug, thiserror::Error)] +// pub enum UdpProxyError { +// #[error("Packet too short. Got {0} bytes")] +// PacketTooShort(usize), +// } + +// /// Extracts information from an original packet, and proxies UDP contents via a new source and destination +// pub async fn proxy_udp_packet<'a>( +// original_packet: IpPacket<'a>, +// new_source: IpAddr, +// new_destination: IpAddr, +// ) -> Result { +// // Parse the original packet's payload to extract UDP data +// let udp_packet = UdpPacket::new(original_packet.get_payload()) +// .ok_or_else(|| UdpProxyError::PacketTooShort(original_packet.get_payload().len()))?; +// log::debug!( +// "Incoming UDP packet ports: {} -> {}", +// udp_packet.get_source(), +// udp_packet.get_destination() +// ); +// log::debug!( +// "Incoming UDP packet payload len: {}", +// udp_packet.payload().len() +// ); + +// // Construct a new output packet +// match (&original_packet, new_source, new_destination) { +// // Translate IPv4(UDP) to IPv6(UDP) +// (IpPacket::V4(_), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => { +// // Construct translated UDP packet +// let mut translated_udp_packet = +// MutableUdpPacket::owned(vec![0u8; 8 + udp_packet.payload().len()]).unwrap(); +// translated_udp_packet.set_source(udp_packet.get_source()); +// translated_udp_packet.set_destination(udp_packet.get_destination()); +// translated_udp_packet.set_length(8 + udp_packet.payload().len() as u16); +// translated_udp_packet.set_payload(udp_packet.payload()); +// translated_udp_packet.set_checksum(0); +// translated_udp_packet.set_checksum(udp::ipv6_checksum( +// &translated_udp_packet.to_immutable(), +// &new_source, +// &new_destination, +// )); + +// // Construct translated IP packet to wrap UDP packet +// let mut output = +// MutableIpv6Packet::owned(vec![0u8; 40 + translated_udp_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::Udp); +// output.set_payload_length(translated_udp_packet.packet().len() as u16); +// output.set_payload(translated_udp_packet.packet()); +// Ok(IpPacket::V6( +// Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), +// )) +// } + +// // Translate IPv6(UDP) to IPv4(UDP) +// (IpPacket::V6(_), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => { +// // Construct translated UDP packet +// let mut translated_udp_packet = +// MutableUdpPacket::owned(vec![0u8; 8 + udp_packet.payload().len()]).unwrap(); +// translated_udp_packet.set_source(udp_packet.get_source()); +// translated_udp_packet.set_destination(udp_packet.get_destination()); +// translated_udp_packet.set_length(8 + udp_packet.payload().len() as u16); +// translated_udp_packet.set_payload(udp_packet.payload()); +// translated_udp_packet.set_checksum(0); +// translated_udp_packet.set_checksum(udp::ipv4_checksum( +// &translated_udp_packet.to_immutable(), +// &new_source, +// &new_destination, +// )); + +// // Construct translated IP packet to wrap UDP packet +// let mut output = +// MutableIpv4Packet::owned(vec![0u8; 20 + translated_udp_packet.packet().len()]) +// .unwrap(); +// output.set_version(4); +// output.set_source(new_source); +// output.set_destination(new_destination); +// output.set_ttl(original_packet.get_ttl()); +// output.set_next_level_protocol(IpNextHeaderProtocols::Udp); +// output.set_header_length(5); +// output.set_total_length(20 + translated_udp_packet.packet().len() as u16); +// output.set_payload(translated_udp_packet.packet()); +// output.set_checksum(0); +// output.set_checksum(ipv4::checksum(&output.to_immutable())); +// Ok(IpPacket::V4( +// Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), +// )) +// } + +// _ => unreachable!(), +// } +// } + +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, + )); + + // Return the translated packet + Ok(UdpPacket::owned(ipv6_udp.packet().to_vec()).unwrap()) } -/// Extracts information from an original packet, and proxies UDP contents via a new source and destination -pub async fn proxy_udp_packet<'a>( - original_packet: IpPacket<'a>, - new_source: IpAddr, - new_destination: IpAddr, -) -> Result { - // Parse the original packet's payload to extract UDP data - let udp_packet = UdpPacket::new(original_packet.get_payload()) - .ok_or_else(|| UdpProxyError::PacketTooShort(original_packet.get_payload().len()))?; - log::debug!("Incoming UDP packet ports: {} -> {}", udp_packet.get_source(), udp_packet.get_destination()); - log::debug!("Incoming UDP packet payload len: {}", udp_packet.payload().len()); +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()))?; - // Construct a new output packet - match (&original_packet, new_source, new_destination) { - // Translate IPv4(UDP) to IPv6(UDP) - (IpPacket::V4(_), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => { - // Construct translated UDP packet - let mut translated_udp_packet = - MutableUdpPacket::owned(vec![0u8; 8 + udp_packet.payload().len()]).unwrap(); - translated_udp_packet.set_source(udp_packet.get_source()); - translated_udp_packet.set_destination(udp_packet.get_destination()); - translated_udp_packet.set_length(8 + udp_packet.payload().len() as u16); - translated_udp_packet.set_payload(udp_packet.payload()); - translated_udp_packet.set_checksum(0); - translated_udp_packet.set_checksum(udp::ipv6_checksum( - &translated_udp_packet.to_immutable(), - &new_source, - &new_destination, - )); + // 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, + )); - // Construct translated IP packet to wrap UDP packet - let mut output = - MutableIpv6Packet::owned(vec![0u8; 40 + translated_udp_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::Udp); - output.set_payload_length(translated_udp_packet.packet().len() as u16); - output.set_payload(translated_udp_packet.packet()); - Ok(IpPacket::V6( - Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), - )) - } - - // Translate IPv6(UDP) to IPv4(UDP) - (IpPacket::V6(_), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => { - // Construct translated UDP packet - let mut translated_udp_packet = - MutableUdpPacket::owned(vec![0u8; 8 + udp_packet.payload().len()]).unwrap(); - translated_udp_packet.set_source(udp_packet.get_source()); - translated_udp_packet.set_destination(udp_packet.get_destination()); - translated_udp_packet.set_length(8 + udp_packet.payload().len() as u16); - translated_udp_packet.set_payload(udp_packet.payload()); - translated_udp_packet.set_checksum(0); - translated_udp_packet.set_checksum(udp::ipv4_checksum( - &translated_udp_packet.to_immutable(), - &new_source, - &new_destination, - )); - - // Construct translated IP packet to wrap UDP packet - let mut output = - MutableIpv4Packet::owned(vec![0u8; 20 + translated_udp_packet.packet().len()]) - .unwrap(); - output.set_version(4); - output.set_source(new_source); - output.set_destination(new_destination); - output.set_ttl(original_packet.get_ttl()); - output.set_next_level_protocol(IpNextHeaderProtocols::Udp); - output.set_header_length(5); - output.set_total_length(20 + translated_udp_packet.packet().len() as u16); - output.set_payload(translated_udp_packet.packet()); - output.set_checksum(0); - output.set_checksum(ipv4::checksum(&output.to_immutable())); - Ok(IpPacket::V4( - Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap(), - )) - } - - _ => unreachable!(), - } + // Return the translated packet + Ok(UdpPacket::owned(ipv4_udp.packet().to_vec()).unwrap()) } diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 5c86265..0000000 --- a/src/types.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; - -use ipnet::Ipv4Net; - -/// Represents a pair of IP addresses for a dual-stack host or mapping -#[derive(Debug, serde::Deserialize)] -pub struct AddressPair { - /// IPv4 address - pub v4: Ipv4Addr, - /// IPv6 address - pub v6: Ipv6Addr, -} - -// /// Represents a pool of IPv4 addresses -// #[derive(Debug, serde::Deserialize)] -// pub struct Ipv4Pool { -// /// All possible addresses -// pub prefixes: Vec, -// /// Addresses that cannot be dynamically assigned -// pub reservations: Vec, -// } - -// impl Ipv4Pool { -// /// Construct a new `Ipv4Pool` -// pub fn new(prefixes: Vec) -> Self { -// Self { -// prefixes, -// reservations: Vec::new(), -// } -// } - -// /// Reserve -// } \ No newline at end of file