Working on UDP proxy
This commit is contained in:
parent
a1ba812a5c
commit
a08b561807
2
Makefile
2
Makefile
@ -1,4 +1,4 @@
|
||||
SRC=$(wildcard src/*.rs) $(wildcard src/**/*.rs) Cargo.toml
|
||||
SRC=$(wildcard src/*.rs) $(wildcard src/**/*.rs) $(wildcard src/**/**/*.rs) Cargo.toml
|
||||
|
||||
target/debug/protomask: $(SRC)
|
||||
cargo build
|
||||
|
@ -11,11 +11,11 @@ use ipnet::{Ipv4Net, Ipv6Net};
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct InterfaceConfig {
|
||||
/// IPv4 router address
|
||||
#[serde(rename = "Address4")]
|
||||
pub address_v4: Ipv4Addr,
|
||||
/// IPv6 router address
|
||||
#[serde(rename = "Address6")]
|
||||
pub address_v6: Ipv6Addr,
|
||||
// #[serde(rename = "Address4")]
|
||||
// pub address_v4: Ipv4Addr,
|
||||
// /// IPv6 router address
|
||||
// #[serde(rename = "Address6")]
|
||||
// pub address_v6: Ipv6Addr,
|
||||
/// Ipv4 pool
|
||||
#[serde(rename = "Pool")]
|
||||
pub pool: Vec<Ipv4Net>,
|
||||
@ -42,7 +42,7 @@ fn default_reservation_duration() -> Duration {
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct RulesConfig {
|
||||
/// Static mapping rules
|
||||
#[serde(rename = "MapStatic")]
|
||||
#[serde(rename = "MapStatic", default="Vec::new")]
|
||||
pub static_map: Vec<AddressMappingRule>,
|
||||
/// How long to hold a dynamic mapping for
|
||||
#[serde(rename = "ReservationDuration", default="default_reservation_duration")]
|
||||
|
@ -6,7 +6,11 @@ use std::{
|
||||
use ipnet::{Ipv4Net, Ipv6Net};
|
||||
use pnet_packet::ip::IpNextHeaderProtocols;
|
||||
|
||||
use self::{interface::Nat64Interface, packet::IpPacket, table::Nat64Table};
|
||||
use self::{
|
||||
interface::Nat64Interface,
|
||||
packet::{IpPacket, PacketError},
|
||||
table::{Nat64Table, TableError},
|
||||
};
|
||||
|
||||
mod interface;
|
||||
mod packet;
|
||||
@ -21,6 +25,8 @@ pub enum Nat64Error {
|
||||
InterfaceError(#[from] interface::InterfaceError),
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
UdpProxyError(#[from] xlat::UdpProxyError),
|
||||
}
|
||||
|
||||
pub struct Nat64 {
|
||||
@ -66,14 +72,27 @@ impl Nat64 {
|
||||
// Parse in to a more friendly format
|
||||
match IpPacket::new(&buffer[..packet_len]) {
|
||||
// Try to process the packet
|
||||
Ok(inbound_packet) => match self.process_packet(inbound_packet).await? {
|
||||
// If data is returned, send it back out the interface
|
||||
Some(outbound_packet) => {
|
||||
let packet_bytes = outbound_packet.to_bytes();
|
||||
self.interface.send(&packet_bytes)?;
|
||||
}
|
||||
// Otherwise, we can assume that the packet was dealt with, and can move on
|
||||
None => {}
|
||||
Ok(inbound_packet) => match self.process_packet(inbound_packet).await {
|
||||
Ok(inbound_packet) => match inbound_packet {
|
||||
// If data is returned, send it back out the interface
|
||||
Some(outbound_packet) => {
|
||||
let packet_bytes = outbound_packet.to_bytes();
|
||||
log::debug!("Sending packet: {:?}", packet_bytes);
|
||||
self.interface.send(&packet_bytes).unwrap();
|
||||
}
|
||||
// Otherwise, we can assume that the packet was dealt with, and can move on
|
||||
None => {}
|
||||
},
|
||||
|
||||
// Some errors are non-critical as far as this loop is concerned
|
||||
Err(error) => match error {
|
||||
Nat64Error::TableError(TableError::NoIpv6Mapping(address)) => {
|
||||
log::debug!("No IPv6 mapping for {}", address);
|
||||
}
|
||||
error => {
|
||||
return Err(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(error) => {
|
||||
log::error!("Failed to parse packet: {}", error);
|
||||
@ -89,10 +108,10 @@ impl Nat64 {
|
||||
}
|
||||
|
||||
impl Nat64 {
|
||||
async fn process_packet(
|
||||
async fn process_packet<'a>(
|
||||
&mut self,
|
||||
packet: IpPacket<'_>,
|
||||
) -> Result<Option<IpPacket>, Nat64Error> {
|
||||
packet: IpPacket<'a>,
|
||||
) -> Result<Option<IpPacket<'a>>, Nat64Error> {
|
||||
// The destination of the packet must be within a prefix we care about
|
||||
if match packet.get_destination() {
|
||||
IpAddr::V4(ipv4_addr) => !self.table.is_address_within_pool(&ipv4_addr),
|
||||
@ -136,7 +155,9 @@ impl Nat64 {
|
||||
match next_header_protocol {
|
||||
IpNextHeaderProtocols::Icmp => unimplemented!(),
|
||||
IpNextHeaderProtocols::Icmpv6 => unimplemented!(),
|
||||
IpNextHeaderProtocols::Udp => unimplemented!(),
|
||||
IpNextHeaderProtocols::Udp => Ok(Some(
|
||||
xlat::proxy_udp_packet(packet, new_source, new_destination).await?,
|
||||
)),
|
||||
IpNextHeaderProtocols::Tcp => unimplemented!(),
|
||||
next_header_protocol => {
|
||||
log::warn!("Unsupported next header protocol: {}", next_header_protocol);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
mod icmp;
|
||||
mod ip;
|
||||
mod udp;
|
||||
|
||||
pub use icmp::{icmpv6_to_icmp, icmp_to_icmpv6};
|
||||
pub use ip::{ipv4_to_ipv6, ipv6_to_ipv4};
|
||||
pub use icmp::{icmp_to_icmpv6, icmpv6_to_icmp};
|
||||
pub use ip::{ipv4_to_ipv6, ipv6_to_ipv4};
|
||||
pub use udp::{proxy_udp_packet,UdpProxyError};
|
||||
|
71
src/nat/xlat/udp.rs
Normal file
71
src/nat/xlat/udp.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
use pnet_packet::{
|
||||
ip::IpNextHeaderProtocols,
|
||||
ipv4::{self, Ipv4Packet, MutableIpv4Packet},
|
||||
ipv6::{Ipv6Packet, MutableIpv6Packet},
|
||||
udp::UdpPacket,
|
||||
Packet,
|
||||
};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::nat::packet::IpPacket;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum UdpProxyError {
|
||||
#[error("Packet too short. Got {0} bytes")]
|
||||
PacketTooShort(usize),
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// 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()))?;
|
||||
|
||||
// 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)) => {
|
||||
let mut output =
|
||||
MutableIpv6Packet::owned(vec![0u8; 40 + 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(udp_packet.packet().len() as u16);
|
||||
output.set_payload(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)) => {
|
||||
let mut output =
|
||||
MutableIpv4Packet::owned(vec![0u8; 20 + 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 + udp_packet.packet().len() as u16);
|
||||
output.set_payload(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!(),
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user