1

Starting the big refactor (cleaned up udp)

This commit is contained in:
Evan Pratten 2023-07-16 22:18:36 -04:00
parent 02e2644d51
commit 37eea69943
8 changed files with 328 additions and 529 deletions

@ -1,5 +1,7 @@
{
"cSpell.words": [
"Datagram",
"pnet",
"rtnetlink"
]
}

@ -6,7 +6,6 @@ use nat::Nat64;
mod cli;
mod config;
mod nat;
mod types;
#[tokio::main]
pub async fn main() {

103
src/nat/macros.rs Normal file

@ -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::<usize>();
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()
}};
}

@ -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)
// }
// }
}
}

@ -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::<Vec<String>>()
.join(" ")
}
pub struct Nat64 {
/// Handle for the Tun interface
interface: Iface,
/// IPv4 pool
ipv4_pool: Vec<Ipv4Net>,
/// IPv6 prefix
ipv6_prefix: Ipv6Net,
/// A mapping of currently allocated pool reservations
pool_reservations: BiMap<Ipv4Addr, Ipv6Addr>,
}
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<Ipv4Net>,
ipv6_prefix: Ipv6Net,
static_mappings: Vec<(Ipv4Addr, Ipv6Addr)>,
) -> Result<Self, std::io::Error> {
// 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<Ipv4Addr> {
// 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<IpAddr> {
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<Option<Vec<u8>>, 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));
}
}

@ -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),
}

@ -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<IpPacket, UdpProxyError> {
// // 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<UdpPacket, PacketTranslationError> {
// 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<IpPacket, UdpProxyError> {
// 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<UdpPacket, PacketTranslationError> {
// 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())
}

@ -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<Ipv4Net>,
// /// Addresses that cannot be dynamically assigned
// pub reservations: Vec<Ipv4Addr>,
// }
// impl Ipv4Pool {
// /// Construct a new `Ipv4Pool`
// pub fn new(prefixes: Vec<Ipv4Net>) -> Self {
// Self {
// prefixes,
// reservations: Vec::new(),
// }
// }
// /// Reserve
// }