Starting the big refactor (cleaned up udp)
This commit is contained in:
parent
02e2644d51
commit
37eea69943
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -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
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()
|
||||
}};
|
||||
}
|
100
src/nat/mod.rs
100
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)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
33
src/types.rs
33
src/types.rs
@ -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
|
||||
// }
|
Loading…
x
Reference in New Issue
Block a user