Deeper ICMP translation
This commit is contained in:
parent
a08b561807
commit
ab254122ce
@ -27,6 +27,10 @@ pub enum Nat64Error {
|
|||||||
IoError(#[from] std::io::Error),
|
IoError(#[from] std::io::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
UdpProxyError(#[from] xlat::UdpProxyError),
|
UdpProxyError(#[from] xlat::UdpProxyError),
|
||||||
|
#[error(transparent)]
|
||||||
|
IcmpProxyError(#[from] xlat::IcmpProxyError),
|
||||||
|
#[error(transparent)]
|
||||||
|
TcpProxyError(#[from] xlat::TcpProxyError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Nat64 {
|
pub struct Nat64 {
|
||||||
@ -77,6 +81,7 @@ impl Nat64 {
|
|||||||
// If data is returned, send it back out the interface
|
// If data is returned, send it back out the interface
|
||||||
Some(outbound_packet) => {
|
Some(outbound_packet) => {
|
||||||
let packet_bytes = outbound_packet.to_bytes();
|
let packet_bytes = outbound_packet.to_bytes();
|
||||||
|
log::debug!("Outbound packet next header: {}", outbound_packet.get_next_header().0);
|
||||||
log::debug!("Sending packet: {:?}", packet_bytes);
|
log::debug!("Sending packet: {:?}", packet_bytes);
|
||||||
self.interface.send(&packet_bytes).unwrap();
|
self.interface.send(&packet_bytes).unwrap();
|
||||||
}
|
}
|
||||||
@ -153,12 +158,15 @@ impl Nat64 {
|
|||||||
next_header_protocol
|
next_header_protocol
|
||||||
);
|
);
|
||||||
match next_header_protocol {
|
match next_header_protocol {
|
||||||
IpNextHeaderProtocols::Icmp => unimplemented!(),
|
IpNextHeaderProtocols::Icmp | IpNextHeaderProtocols::Icmpv6 => Ok(
|
||||||
IpNextHeaderProtocols::Icmpv6 => unimplemented!(),
|
xlat::proxy_icmp_packet(packet, new_source, new_destination)?,
|
||||||
|
),
|
||||||
IpNextHeaderProtocols::Udp => Ok(Some(
|
IpNextHeaderProtocols::Udp => Ok(Some(
|
||||||
xlat::proxy_udp_packet(packet, new_source, new_destination).await?,
|
xlat::proxy_udp_packet(packet, new_source, new_destination).await?,
|
||||||
)),
|
)),
|
||||||
IpNextHeaderProtocols::Tcp => unimplemented!(),
|
IpNextHeaderProtocols::Tcp => Ok(Some(
|
||||||
|
xlat::proxy_tcp_packet(packet, new_source, new_destination).await?,
|
||||||
|
)),
|
||||||
next_header_protocol => {
|
next_header_protocol => {
|
||||||
log::warn!("Unsupported next header protocol: {}", next_header_protocol);
|
log::warn!("Unsupported next header protocol: {}", next_header_protocol);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -1,177 +1,329 @@
|
|||||||
//! Translation logic for ICMP and ICMPv6
|
//! Translation logic for ICMP and ICMPv6
|
||||||
|
|
||||||
use std::net::Ipv6Addr;
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
use colored::Colorize;
|
|
||||||
use pnet_packet::{
|
use pnet_packet::{
|
||||||
icmp::{self, Icmp, IcmpCode, IcmpPacket, IcmpType, MutableIcmpPacket},
|
icmp::{self, IcmpCode, IcmpPacket, IcmpType, MutableIcmpPacket},
|
||||||
icmpv6::{self, Icmpv6Packet, MutableIcmpv6Packet, Icmpv6, Icmpv6Type, Icmpv6Code},
|
icmpv6::{self, Icmpv6Code, Icmpv6Packet, Icmpv6Type, MutableIcmpv6Packet},
|
||||||
|
ip::IpNextHeaderProtocols,
|
||||||
|
ipv4::{self, Ipv4Packet, MutableIpv4Packet},
|
||||||
|
ipv6::{Ipv6Packet, MutableIpv6Packet},
|
||||||
Packet,
|
Packet,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn icmpv6_to_icmp<'a>(input: &'a Icmpv6Packet<'a>) -> Option<IcmpPacket<'a>> {
|
use crate::nat::packet::IpPacket;
|
||||||
let data = match input.get_icmpv6_type().0 {
|
|
||||||
|
fn remap_values_4to6(
|
||||||
|
icmp_type: IcmpType,
|
||||||
|
icmp_code: IcmpCode,
|
||||||
|
new_source: Ipv6Addr,
|
||||||
|
new_destination: Ipv6Addr,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> Option<(Icmpv6Type, Icmpv6Code, Vec<u8>)> {
|
||||||
|
match icmp_type {
|
||||||
// Destination Unreachable
|
// Destination Unreachable
|
||||||
1 => Icmp {
|
IcmpType(3) => match icmp_code {
|
||||||
icmp_type: IcmpType(3),
|
IcmpCode(0) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination network unreachable -> No route to destination
|
||||||
// A best guess translation of ICMP codes. Feel free to open a PR to improve this :)
|
IcmpCode(1) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Destination host unreachable -> Address unreachable
|
||||||
icmp_code: IcmpCode(match input.get_icmpv6_code().0 {
|
IcmpCode(2) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination protocol unreachable -> No route to destination
|
||||||
// No route to destination -> Destination network unreachable
|
IcmpCode(3) => Some((Icmpv6Type(1), Icmpv6Code(4), payload)), // Destination port unreachable -> Port unreachable
|
||||||
0 => 0,
|
IcmpCode(4) => Some((Icmpv6Type(2), Icmpv6Code(0), vec![])), // Fragmentation required, and DF flag set -> Packet too big
|
||||||
// Communication with destination administratively prohibited -> Communication administratively prohibited
|
IcmpCode(5) => Some((Icmpv6Type(1), Icmpv6Code(5), payload)), // Source route failed -> Source address failed ingress/egress policy
|
||||||
1 => 13,
|
IcmpCode(6) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Destination network unknown -> No route to destination
|
||||||
// Beyond scope of source address -> Destination network unreachable
|
IcmpCode(7) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Destination host unknown -> Address unreachable
|
||||||
2 => 0,
|
IcmpCode(8) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Source host isolated -> No route to destination
|
||||||
// Address unreachable -> Destination host unreachable
|
IcmpCode(9) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Network administratively prohibited -> Communication with destination administratively prohibited
|
||||||
3 => 1,
|
IcmpCode(10) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Host administratively prohibited -> Communication with destination administratively prohibited
|
||||||
// Port unreachable -> Destination port unreachable
|
IcmpCode(11) => Some((Icmpv6Type(1), Icmpv6Code(0), payload)), // Network unreachable for ToS -> No route to destination
|
||||||
4 => 3,
|
IcmpCode(12) => Some((Icmpv6Type(1), Icmpv6Code(3), payload)), // Host unreachable for ToS -> Address unreachable
|
||||||
// Source address failed ingress/egress policy -> Source route failed
|
IcmpCode(13) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Communication administratively prohibited -> Communication with destination administratively prohibited
|
||||||
5 => 5,
|
IcmpCode(14) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Host Precedence Violation -> Communication with destination administratively prohibited
|
||||||
// Reject route to destination -> Destination network unreachable
|
IcmpCode(15) => Some((Icmpv6Type(1), Icmpv6Code(1), payload)), // Precedence cutoff in effect -> Communication with destination administratively prohibited
|
||||||
6 => 0,
|
_ => Some((Icmpv6Type(1), Icmpv6Code(0), payload)),
|
||||||
// Error in Source Routing Header -> Destination network unreachable
|
|
||||||
7 => 0,
|
|
||||||
// All others -> Destination network unreachable
|
|
||||||
_ => 0,
|
|
||||||
}),
|
|
||||||
checksum: 0,
|
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Time Exceeded
|
// Time Exceeded
|
||||||
3 => Icmp {
|
IcmpType(11) => Some((Icmpv6Type(3), Icmpv6Code(icmp_code.0), {
|
||||||
icmp_type: IcmpType(11),
|
// The payload contains an IPv4 header and 8 bytes of data. This must also be translated
|
||||||
icmp_code: IcmpCode(input.get_icmpv6_code().0),
|
let embedded_ipv4_packet = Ipv4Packet::new(&payload[4..]).unwrap();
|
||||||
checksum: 0,
|
log::debug!("Embedded payload is: {:?}", embedded_ipv4_packet.payload());
|
||||||
payload: input.payload().to_vec(),
|
log::debug!(
|
||||||
},
|
"Embedded next level protocol is: {}",
|
||||||
|
embedded_ipv4_packet.get_next_level_protocol().0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build an IPv6 packet out of the IPv4 packet
|
||||||
|
let mut embedded_ipv6_packet =
|
||||||
|
MutableIpv6Packet::owned(vec![0u8; 40 + 4 + 8])
|
||||||
|
.unwrap();
|
||||||
|
embedded_ipv6_packet.set_version(6);
|
||||||
|
embedded_ipv6_packet.set_source(new_source);
|
||||||
|
embedded_ipv6_packet.set_destination(new_destination);
|
||||||
|
embedded_ipv6_packet.set_hop_limit(embedded_ipv4_packet.get_ttl());
|
||||||
|
embedded_ipv6_packet.set_next_header(
|
||||||
|
match embedded_ipv4_packet.get_next_level_protocol() {
|
||||||
|
IpNextHeaderProtocols::Icmp => IpNextHeaderProtocols::Icmpv6,
|
||||||
|
proto => proto,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// embedded_ipv6_packet.set_payload_length(embedded_ipv4_packet.payload().len() as u16);
|
||||||
|
embedded_ipv6_packet.set_payload_length(8u16);
|
||||||
|
|
||||||
|
// Handle translating the embedded packet if it's ICMP
|
||||||
|
match embedded_ipv4_packet.get_next_level_protocol() {
|
||||||
|
IpNextHeaderProtocols::Icmp => {
|
||||||
|
let embedded_ipv4_packet_payload_bytes = embedded_ipv4_packet.payload();
|
||||||
|
let embedded_icmp_type = IcmpType(embedded_ipv4_packet_payload_bytes[0]);
|
||||||
|
let embedded_icmp_code = IcmpCode(embedded_ipv4_packet_payload_bytes[1]);
|
||||||
|
let embedded_icmp_payload = &embedded_ipv4_packet_payload_bytes[4..];
|
||||||
|
|
||||||
|
// Translate from ICMP to ICMPv6
|
||||||
|
let (embedded_icmpv6_type, embedded_icmpv6_code, embedded_icmpv6_payload) =
|
||||||
|
remap_values_4to6(
|
||||||
|
embedded_icmp_type,
|
||||||
|
embedded_icmp_code,
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
embedded_icmp_payload.to_vec(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Build an ICMPv6 packet out of the ICMPv6 values
|
||||||
|
let mut double_embedded_icmpv6_packet = MutableIcmpv6Packet::owned(vec![
|
||||||
|
0u8;
|
||||||
|
Icmpv6Packet::minimum_packet_size()
|
||||||
|
+ embedded_icmpv6_payload.len()
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
double_embedded_icmpv6_packet.set_icmpv6_type(embedded_icmpv6_type);
|
||||||
|
double_embedded_icmpv6_packet.set_icmpv6_code(embedded_icmpv6_code);
|
||||||
|
double_embedded_icmpv6_packet.set_payload(&embedded_icmpv6_payload);
|
||||||
|
double_embedded_icmpv6_packet.set_checksum(0);
|
||||||
|
double_embedded_icmpv6_packet.set_checksum(icmpv6::checksum(
|
||||||
|
&double_embedded_icmpv6_packet.to_immutable(),
|
||||||
|
&new_source,
|
||||||
|
&new_destination,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Return the first 8 bytes of the embedded icmpv6 packet
|
||||||
|
embedded_ipv6_packet.set_payload(&double_embedded_icmpv6_packet.packet()[..8]);
|
||||||
|
}
|
||||||
|
_ => embedded_ipv6_packet.set_payload(embedded_ipv4_packet.payload()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the IPv6 packet
|
||||||
|
embedded_ipv6_packet.packet().to_vec()
|
||||||
|
})),
|
||||||
|
|
||||||
// Echo Request
|
// Echo Request
|
||||||
128 => Icmp {
|
IcmpType(8) => Some((Icmpv6Type(128), Icmpv6Code(0), payload)),
|
||||||
icmp_type: IcmpType(8),
|
|
||||||
icmp_code: IcmpCode(0),
|
|
||||||
checksum: 0,
|
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
|
||||||
// Echo Reply
|
|
||||||
129 => Icmp {
|
|
||||||
icmp_type: IcmpType(0),
|
|
||||||
icmp_code: IcmpCode(0),
|
|
||||||
checksum: 0,
|
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
log::warn!("ICMPv6 type {} not supported", input.get_icmpv6_type().0);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Debug logging
|
|
||||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
|
||||||
{
|
|
||||||
log::debug!("> Input ICMP Type: {}", input.get_icmpv6_type().0.to_string().bright_cyan());
|
|
||||||
log::debug!("> Input ICMP Code: {}", input.get_icmpv6_code().0.to_string().bright_cyan());
|
|
||||||
log::debug!("> Output ICMP Type: {}", data.icmp_type.0.to_string().bright_cyan());
|
|
||||||
log::debug!("> Output ICMP Code: {}", data.icmp_code.0.to_string().bright_cyan());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new ICMP packet
|
|
||||||
let mut output = MutableIcmpPacket::owned(vec![0u8; IcmpPacket::packet_size(&data)]).unwrap();
|
|
||||||
output.populate(&data);
|
|
||||||
output.set_checksum(icmp::checksum(&output.to_immutable()));
|
|
||||||
|
|
||||||
IcmpPacket::owned(output.to_immutable().packet().to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icmp_to_icmpv6<'a>(
|
|
||||||
input: &'a IcmpPacket<'a>,
|
|
||||||
source: &Ipv6Addr,
|
|
||||||
dest: &Ipv6Addr,
|
|
||||||
) -> Option<Icmpv6Packet<'a>> {
|
|
||||||
let data = match input.get_icmp_type().0 {
|
|
||||||
// Destination Unreachable
|
|
||||||
3 => Icmpv6 {
|
|
||||||
icmpv6_type: Icmpv6Type(1),
|
|
||||||
// A best guess translation of ICMP codes. Feel free to open a PR to improve this :)
|
|
||||||
icmpv6_code: Icmpv6Code(match input.get_icmp_code().0 {
|
|
||||||
// Destination network unreachable -> No route to destination
|
|
||||||
0 => 0,
|
|
||||||
// Destination host unreachable -> Address unreachable
|
|
||||||
1 => 3,
|
|
||||||
// Destination protocol unreachable -> No route to destination
|
|
||||||
2 => 0,
|
|
||||||
// Destination port unreachable -> Port unreachable
|
|
||||||
3 => 4,
|
|
||||||
// Fragmentation required, and DF flag set -> Packet too big
|
|
||||||
4 => 2,
|
|
||||||
// Source route failed -> Source address failed ingress/egress policy
|
|
||||||
5 => 5,
|
|
||||||
// Destination network unknown -> No route to destination
|
|
||||||
6 => 0,
|
|
||||||
// Destination host unknown -> Address unreachable
|
|
||||||
7 => 3,
|
|
||||||
// Source host isolated -> No route to destination
|
|
||||||
8 => 0,
|
|
||||||
// Network administratively prohibited -> Communication with destination administratively prohibited
|
|
||||||
9 => 1,
|
|
||||||
// Host administratively prohibited -> Communication with destination administratively prohibited
|
|
||||||
10 => 1,
|
|
||||||
// Network unreachable for ToS -> No route to destination
|
|
||||||
11 => 0,
|
|
||||||
// Host unreachable for ToS -> Address unreachable
|
|
||||||
12 => 3,
|
|
||||||
// Communication administratively prohibited -> Communication with destination administratively prohibited
|
|
||||||
13 => 1,
|
|
||||||
// Host Precedence Violation -> Communication with destination administratively prohibited
|
|
||||||
14 => 1,
|
|
||||||
// Precedence cutoff in effect -> Communication with destination administratively prohibited
|
|
||||||
15 => 1,
|
|
||||||
// All others -> No route to destination
|
|
||||||
_ => 0,
|
|
||||||
}),
|
|
||||||
checksum: 0,
|
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
|
||||||
// Time Exceeded
|
|
||||||
11 => Icmpv6 {
|
|
||||||
icmpv6_type: Icmpv6Type(3),
|
|
||||||
icmpv6_code: Icmpv6Code(input.get_icmp_code().0),
|
|
||||||
checksum: 0,
|
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
|
||||||
// Echo Request
|
|
||||||
8 => Icmpv6 {
|
|
||||||
icmpv6_type: Icmpv6Type(128),
|
|
||||||
icmpv6_code: Icmpv6Code(0),
|
|
||||||
checksum: 0,
|
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Echo Reply
|
// Echo Reply
|
||||||
0 => Icmpv6 {
|
IcmpType(0) => Some((Icmpv6Type(129), Icmpv6Code(0), payload)),
|
||||||
icmpv6_type: Icmpv6Type(129),
|
|
||||||
icmpv6_code: Icmpv6Code(0),
|
icmp_type => {
|
||||||
checksum: 0,
|
log::warn!("ICMP type {} not supported", icmp_type.0);
|
||||||
payload: input.payload().to_vec(),
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
log::warn!("ICMP type {} not supported", input.get_icmp_type().0);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Debug logging
|
|
||||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
|
||||||
{
|
|
||||||
log::debug!("> Input ICMP Type: {}", input.get_icmp_type().0.to_string().bright_cyan());
|
|
||||||
log::debug!("> Input ICMP Code: {}", input.get_icmp_code().0.to_string().bright_cyan());
|
|
||||||
log::debug!("> Output ICMP Type: {}", data.icmpv6_type.0.to_string().bright_cyan());
|
|
||||||
log::debug!("> Output ICMP Code: {}", data.icmpv6_code.0.to_string().bright_cyan());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Create new ICMP packet
|
|
||||||
let mut output = MutableIcmpv6Packet::owned(vec![0u8; Icmpv6Packet::packet_size(&data)]).unwrap();
|
fn remap_values_6to4(
|
||||||
output.populate(&data);
|
icmp_type: Icmpv6Type,
|
||||||
output.set_checksum(icmpv6::checksum(&output.to_immutable(), source, dest));
|
icmp_code: Icmpv6Code,
|
||||||
|
new_source: Ipv4Addr,
|
||||||
Icmpv6Packet::owned(output.to_immutable().packet().to_vec())
|
new_destination: Ipv4Addr,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> Option<(IcmpType, IcmpCode, Vec<u8>)> {
|
||||||
|
match icmp_type {
|
||||||
|
// Destination Unreachable
|
||||||
|
Icmpv6Type(1) => match icmp_code {
|
||||||
|
Icmpv6Code(0) => Some((IcmpType(3), IcmpCode(0), payload)), // No route to destination -> Destination network unreachable
|
||||||
|
Icmpv6Code(3) => Some((IcmpType(3), IcmpCode(1), payload)), // Address unreachable -> Destination host unreachable
|
||||||
|
Icmpv6Code(4) => Some((IcmpType(3), IcmpCode(3), payload)), // Port unreachable -> Destination port unreachable
|
||||||
|
Icmpv6Code(5) => Some((IcmpType(3), IcmpCode(5), payload)), // Source route failed -> Source address failed ingress/egress policy
|
||||||
|
Icmpv6Code(1) => Some((IcmpType(3), IcmpCode(13), payload)), // Communication administratively prohibited -> Communication administratively prohibited
|
||||||
|
_ => Some((IcmpType(3), IcmpCode(0), payload)),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Time Exceeded
|
||||||
|
Icmpv6Type(3) => Some((IcmpType(11), IcmpCode(icmp_code.0), {
|
||||||
|
// The payload contains an IPv6 header and 8 bytes of data. This must also be translated
|
||||||
|
let embedded_ipv6_packet = Ipv6Packet::new(&payload).unwrap();
|
||||||
|
log::debug!("Embedded payload is: {:?}", embedded_ipv6_packet.payload());
|
||||||
|
log::debug!(
|
||||||
|
"Embedded next header is: {}",
|
||||||
|
embedded_ipv6_packet.get_next_header().0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build an IPv4 packet out of the IPv6 packet
|
||||||
|
let mut embedded_ipv4_packet =
|
||||||
|
MutableIpv4Packet::owned(vec![0u8; 20 + embedded_ipv6_packet.payload().len()])
|
||||||
|
.unwrap();
|
||||||
|
embedded_ipv4_packet.set_version(4);
|
||||||
|
embedded_ipv4_packet.set_source(new_source);
|
||||||
|
embedded_ipv4_packet.set_destination(new_destination);
|
||||||
|
embedded_ipv4_packet.set_ttl(embedded_ipv6_packet.get_hop_limit());
|
||||||
|
embedded_ipv4_packet.set_next_level_protocol(
|
||||||
|
match embedded_ipv6_packet.get_next_header() {
|
||||||
|
IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp,
|
||||||
|
proto => proto,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
embedded_ipv4_packet.set_header_length(5);
|
||||||
|
embedded_ipv4_packet.set_total_length(20 + embedded_ipv6_packet.payload().len() as u16);
|
||||||
|
embedded_ipv4_packet.set_payload(embedded_ipv6_packet.payload());
|
||||||
|
embedded_ipv4_packet.set_checksum(0);
|
||||||
|
embedded_ipv4_packet.set_checksum(ipv4::checksum(&embedded_ipv4_packet.to_immutable()));
|
||||||
|
|
||||||
|
// Return the IPv4 packet
|
||||||
|
embedded_ipv4_packet.packet().to_vec()
|
||||||
|
})),
|
||||||
|
|
||||||
|
// Echo Request
|
||||||
|
Icmpv6Type(128) => Some((IcmpType(8), IcmpCode(0), payload)),
|
||||||
|
|
||||||
|
// Echo Reply
|
||||||
|
Icmpv6Type(129) => Some((IcmpType(0), IcmpCode(0), payload)),
|
||||||
|
|
||||||
|
icmp_type => {
|
||||||
|
log::warn!("ICMPv6 type {} not supported", icmp_type.0);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum IcmpProxyError {
|
||||||
|
#[error("Packet too short. Got {0} bytes")]
|
||||||
|
PacketTooShort(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn proxy_icmp_packet<'a>(
|
||||||
|
original_packet: IpPacket<'a>,
|
||||||
|
new_source: IpAddr,
|
||||||
|
new_destination: IpAddr,
|
||||||
|
) -> Result<Option<IpPacket>, IcmpProxyError> {
|
||||||
|
// Parse the original packet's payload to extract ICMP data
|
||||||
|
let icmp_packet = original_packet.get_payload().to_vec();
|
||||||
|
|
||||||
|
// Construct a new output packet
|
||||||
|
match (original_packet, new_source, new_destination) {
|
||||||
|
// Translate IPv4(ICMP) to IPv6(ICMPv6)
|
||||||
|
(IpPacket::V4(original_packet), IpAddr::V6(new_source), IpAddr::V6(new_destination)) => {
|
||||||
|
// Parse the ICMP packet
|
||||||
|
let icmp_packet = IcmpPacket::new(&icmp_packet)
|
||||||
|
.ok_or_else(|| IcmpProxyError::PacketTooShort(icmp_packet.len()))?;
|
||||||
|
log::debug!(
|
||||||
|
"Incoming packet has ICMP type: {}",
|
||||||
|
icmp_packet.get_icmp_type().0
|
||||||
|
);
|
||||||
|
log::debug!(
|
||||||
|
"Incoming packet has ICMP code: {}",
|
||||||
|
icmp_packet.get_icmp_code().0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remap ICMP values to ICMPv6 ones
|
||||||
|
if let Some((icmpv6_type, icmpv6_code, icmpv6_payload)) = remap_values_4to6(
|
||||||
|
icmp_packet.get_icmp_type(),
|
||||||
|
icmp_packet.get_icmp_code(),
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
icmp_packet.payload().to_vec(),
|
||||||
|
) {
|
||||||
|
// Build an actual ICMPv6 packet out of the values
|
||||||
|
let mut icmpv6_packet = MutableIcmpv6Packet::owned(vec![
|
||||||
|
0u8;
|
||||||
|
Icmpv6Packet::minimum_packet_size()
|
||||||
|
+ icmpv6_payload.len()
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
icmpv6_packet.set_icmpv6_type(icmpv6_type);
|
||||||
|
icmpv6_packet.set_icmpv6_code(icmpv6_code);
|
||||||
|
icmpv6_packet.set_payload(&icmpv6_payload);
|
||||||
|
icmpv6_packet.set_checksum(0);
|
||||||
|
icmpv6_packet.set_checksum(icmpv6::checksum(
|
||||||
|
&icmpv6_packet.to_immutable(),
|
||||||
|
&new_source,
|
||||||
|
&new_destination,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Build an IPv6 packet out of the ICMPv6 packet
|
||||||
|
let mut output =
|
||||||
|
MutableIpv6Packet::owned(vec![0u8; 40 + icmpv6_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::Icmpv6);
|
||||||
|
output.set_payload_length(icmpv6_packet.packet().len() as u16);
|
||||||
|
output.set_payload(icmpv6_packet.packet());
|
||||||
|
|
||||||
|
// Return the IPv6 packet
|
||||||
|
return Ok(Some(IpPacket::V6(
|
||||||
|
Ipv6Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate IPv6(ICMPv6) to IPv4(ICMP)
|
||||||
|
(IpPacket::V6(original_packet), IpAddr::V4(new_source), IpAddr::V4(new_destination)) => {
|
||||||
|
// Parse the ICMP packet
|
||||||
|
let icmp_packet = Icmpv6Packet::new(&icmp_packet)
|
||||||
|
.ok_or_else(|| IcmpProxyError::PacketTooShort(icmp_packet.len()))?;
|
||||||
|
log::debug!(
|
||||||
|
"Incoming packet has ICMPv6 type: {}",
|
||||||
|
icmp_packet.get_icmpv6_type().0
|
||||||
|
);
|
||||||
|
log::debug!(
|
||||||
|
"Incoming packet has ICMPv6 code: {}",
|
||||||
|
icmp_packet.get_icmpv6_code().0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remap ICMPv6 values to ICMP ones
|
||||||
|
if let Some((icmp_type, icmp_code, icmp_payload)) = remap_values_6to4(
|
||||||
|
icmp_packet.get_icmpv6_type(),
|
||||||
|
icmp_packet.get_icmpv6_code(),
|
||||||
|
new_source,
|
||||||
|
new_destination,
|
||||||
|
icmp_packet.payload().to_vec(),
|
||||||
|
) {
|
||||||
|
// Build an actual ICMP packet out of the values
|
||||||
|
let mut icmp_packet = MutableIcmpPacket::owned(vec![
|
||||||
|
0u8;
|
||||||
|
IcmpPacket::minimum_packet_size(
|
||||||
|
) + icmp_payload.len()
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
icmp_packet.set_icmp_type(icmp_type);
|
||||||
|
icmp_packet.set_icmp_code(icmp_code);
|
||||||
|
icmp_packet.set_payload(&icmp_payload);
|
||||||
|
icmp_packet.set_checksum(0);
|
||||||
|
icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable()));
|
||||||
|
|
||||||
|
// Build an IPv4 packet out of the ICMP packet
|
||||||
|
let mut output =
|
||||||
|
MutableIpv4Packet::owned(vec![0u8; 20 + icmp_packet.packet().len()]).unwrap();
|
||||||
|
output.set_version(4);
|
||||||
|
output.set_source(new_source);
|
||||||
|
output.set_destination(new_destination);
|
||||||
|
output.set_ttl(original_packet.get_hop_limit());
|
||||||
|
output.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
|
||||||
|
output.set_header_length(5);
|
||||||
|
output.set_total_length(20 + icmp_packet.packet().len() as u16);
|
||||||
|
output.set_payload(icmp_packet.packet());
|
||||||
|
output.set_checksum(0);
|
||||||
|
output.set_checksum(ipv4::checksum(&output.to_immutable()));
|
||||||
|
|
||||||
|
// Return the IPv4 packet
|
||||||
|
return Ok(Some(IpPacket::V4(
|
||||||
|
Ipv4Packet::owned(output.to_immutable().packet().to_vec()).unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
mod icmp;
|
mod icmp;
|
||||||
mod ip;
|
mod ip;
|
||||||
|
mod tcp;
|
||||||
mod udp;
|
mod udp;
|
||||||
|
|
||||||
pub use icmp::{icmp_to_icmpv6, icmpv6_to_icmp};
|
pub use icmp::{proxy_icmp_packet, IcmpProxyError};
|
||||||
pub use ip::{ipv4_to_ipv6, ipv6_to_ipv4};
|
pub use ip::{ipv4_to_ipv6, ipv6_to_ipv4};
|
||||||
pub use udp::{proxy_udp_packet,UdpProxyError};
|
pub use tcp::{proxy_tcp_packet, TcpProxyError};
|
||||||
|
pub use udp::{proxy_udp_packet, UdpProxyError};
|
||||||
|
67
src/nat/xlat/tcp.rs
Normal file
67
src/nat/xlat/tcp.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use pnet_packet::{
|
||||||
|
ip::IpNextHeaderProtocols,
|
||||||
|
ipv4::{self, Ipv4Packet, MutableIpv4Packet},
|
||||||
|
ipv6::{Ipv6Packet, MutableIpv6Packet},
|
||||||
|
Packet, tcp::TcpPacket,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::nat::packet::IpPacket;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum TcpProxyError {
|
||||||
|
#[error("Packet too short. Got {0} bytes")]
|
||||||
|
PacketTooShort(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts information from an original packet, and proxies TCP contents via a new source and destination
|
||||||
|
pub async fn proxy_tcp_packet<'a>(
|
||||||
|
original_packet: IpPacket<'a>,
|
||||||
|
new_source: IpAddr,
|
||||||
|
new_destination: IpAddr,
|
||||||
|
) -> Result<IpPacket, TcpProxyError> {
|
||||||
|
// Parse the original packet's payload to extract UDP data
|
||||||
|
let tcp_packet = TcpPacket::new(original_packet.get_payload())
|
||||||
|
.ok_or_else(|| TcpProxyError::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 + tcp_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(tcp_packet.packet().len() as u16);
|
||||||
|
output.set_payload(tcp_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 + tcp_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 + tcp_packet.packet().len() as u16);
|
||||||
|
output.set_payload(tcp_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!(),
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use pnet_packet::{
|
use pnet_packet::{
|
||||||
ip::IpNextHeaderProtocols,
|
ip::IpNextHeaderProtocols,
|
||||||
@ -7,7 +7,6 @@ use pnet_packet::{
|
|||||||
udp::UdpPacket,
|
udp::UdpPacket,
|
||||||
Packet,
|
Packet,
|
||||||
};
|
};
|
||||||
use tokio::net::UdpSocket;
|
|
||||||
|
|
||||||
use crate::nat::packet::IpPacket;
|
use crate::nat::packet::IpPacket;
|
||||||
|
|
||||||
@ -15,8 +14,6 @@ use crate::nat::packet::IpPacket;
|
|||||||
pub enum UdpProxyError {
|
pub enum UdpProxyError {
|
||||||
#[error("Packet too short. Got {0} bytes")]
|
#[error("Packet too short. Got {0} bytes")]
|
||||||
PacketTooShort(usize),
|
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
|
/// Extracts information from an original packet, and proxies UDP contents via a new source and destination
|
||||||
|
Loading…
x
Reference in New Issue
Block a user