1

ICMP NAT!

This commit is contained in:
Evan Pratten 2023-07-15 21:32:16 -04:00
parent fc567c63ff
commit 397943bbc4
4 changed files with 70 additions and 39 deletions

19
debug.sh Normal file
View File

@ -0,0 +1,19 @@
#! /bin/bash
# A little script to isolate and run protomask for testing
set -ex
# Set up network namespace
ip netns del protomask || true
ip netns add protomask
ip netns exec protomask ip link set lo up
ip netns exec protomask ip link add test1 type dummy
ip netns exec protomask ip link set test1 up
ip netns exec protomask ip addr add 2001:db8:1::2 dev test1
ip netns exec protomask ip link add test2 type dummy
ip netns exec protomask ip link set test2 up
ip netns exec protomask ip addr add 172.16.10.2 dev test2
# Run protomask
ip netns exec protomask ./target/debug/protomask protomask.toml -v

View File

@ -1,9 +1,11 @@
//! ICMP packets require their own translation system
use std::net::Ipv6Addr;
use colored::Colorize;
use pnet_packet::{
icmp::{self, Icmp, IcmpCode, IcmpPacket, IcmpType, MutableIcmpPacket},
icmpv6::Icmpv6Packet,
icmpv6::{self, Icmpv6Packet, MutableIcmpv6Packet, Icmpv6, Icmpv6Type, Icmpv6Code},
Packet,
};
@ -80,13 +82,17 @@ pub fn icmpv6_to_icmp<'a>(input: &'a Icmpv6Packet<'a>) -> Option<IcmpPacket<'a>>
IcmpPacket::owned(output.to_immutable().packet().to_vec())
}
pub fn icmp_to_icmpv6<'a>(input: &'a IcmpPacket<'a>) -> Option<Icmpv6Packet<'a>> {
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 => Icmp {
icmp_type: IcmpType(1),
3 => Icmpv6 {
icmpv6_type: Icmpv6Type(1),
// A best guess translation of ICMP codes. Feel free to open a PR to improve this :)
icmp_code: IcmpCode(match input.get_icmp_code().0 {
icmpv6_code: Icmpv6Code(match input.get_icmp_code().0 {
// Destination network unreachable -> No route to destination
0 => 0,
// Destination host unreachable -> Address unreachable
@ -126,24 +132,24 @@ pub fn icmp_to_icmpv6<'a>(input: &'a IcmpPacket<'a>) -> Option<Icmpv6Packet<'a>>
payload: input.payload().to_vec(),
},
// Time Exceeded
11 => Icmp {
icmp_type: IcmpType(3),
icmp_code: IcmpCode(input.get_icmp_code().0),
11 => Icmpv6 {
icmpv6_type: Icmpv6Type(3),
icmpv6_code: Icmpv6Code(input.get_icmp_code().0),
checksum: 0,
payload: input.payload().to_vec(),
},
// Echo Request
8 => Icmp {
icmp_type: IcmpType(128),
icmp_code: IcmpCode(0),
8 => Icmpv6 {
icmpv6_type: Icmpv6Type(128),
icmpv6_code: Icmpv6Code(0),
checksum: 0,
payload: input.payload().to_vec(),
},
// Echo Reply
0 => Icmp {
icmp_type: IcmpType(129),
icmp_code: IcmpCode(0),
0 => Icmpv6 {
icmpv6_type: Icmpv6Type(129),
icmpv6_code: Icmpv6Code(0),
checksum: 0,
payload: input.payload().to_vec(),
},
@ -158,14 +164,14 @@ pub fn icmp_to_icmpv6<'a>(input: &'a IcmpPacket<'a>) -> Option<Icmpv6Packet<'a>>
{
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.icmp_type.0.to_string().bright_cyan());
log::debug!("> Output ICMP Code: {}", data.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 = MutableIcmpPacket::owned(vec![0u8; IcmpPacket::packet_size(&data)]).unwrap();
let mut output = MutableIcmpv6Packet::owned(vec![0u8; Icmpv6Packet::packet_size(&data)]).unwrap();
output.populate(&data);
output.set_checksum(icmp::checksum(&output.to_immutable()));
output.set_checksum(icmpv6::checksum(&output.to_immutable(), source, dest));
Icmpv6Packet::owned(output.to_immutable().packet().to_vec())
}

View File

@ -4,10 +4,12 @@ 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, ipv4::{Ipv4Packet, MutableIpv4Packet}, icmp::IcmpPacket,
Packet,
};
use tokio::process::Command;
use tun_tap::{Iface, Mode};
@ -282,26 +284,31 @@ impl Nat64 {
}
// 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) =
icmp::icmp_to_icmpv6(&IcmpPacket::new(packet.payload()).unwrap())
{
// 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.packet().to_vec()
} else {
return Ok(None);
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) = icmp::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(),
}) {
// By default, packets can be directly fed to the next function
_ => packet.packet().to_vec(),
})
{
// Translate the packet
let translated = xlat_v4_to_v6(&packet, new_source, new_dest, true);
@ -316,7 +323,6 @@ impl Nat64 {
} else {
return Ok(None);
}
} else {
return Ok(None);
}

View File

@ -139,7 +139,7 @@ pub fn xlat_v4_to_v6(
version: 6,
traffic_class: 0,
flow_label: 0,
payload_length: 40 + ipv4_packet.payload().len() as u16,
payload_length: ipv4_packet.payload().len() as u16,
next_header: ipv4_packet.get_next_level_protocol(),
hop_limit: ipv4_packet.get_ttl(),
source: new_source,