CLAT is functional
This commit is contained in:
parent
833d4308fb
commit
74ad55bde5
@ -37,7 +37,7 @@ pub fn translate_icmp_to_icmpv6(
|
||||
Icmpv6Types::TimeExceeded => {
|
||||
// Time exceeded messages contain the original IPv4 header and part of the payload. (with 4 bytes of forward padding)
|
||||
// We need to translate the IPv4 header and the payload, but keep the padding
|
||||
let mut output = vec![0u8; icmp_packet.payload().len()];
|
||||
let mut output = vec![0u8; 4];
|
||||
output.copy_from_slice(&icmp_packet.payload()[..4]);
|
||||
output.extend_from_slice(&translate_ipv4_to_ipv6(
|
||||
&icmp_packet.payload()[4..],
|
||||
@ -98,7 +98,7 @@ pub fn translate_icmpv6_to_icmp(
|
||||
IcmpTypes::TimeExceeded => {
|
||||
// Time exceeded messages contain the original IPv6 header and part of the payload. (with 4 bytes of forward padding)
|
||||
// We need to translate the IPv6 header and the payload, but keep the padding
|
||||
let mut output = vec![0u8; icmpv6_packet.payload().len()];
|
||||
let mut output = vec![0u8; 4];
|
||||
output.copy_from_slice(&icmpv6_packet.payload()[..4]);
|
||||
output.extend_from_slice(&translate_ipv6_to_ipv4(
|
||||
&icmpv6_packet.payload()[4..],
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Common code used across all protomask binaries
|
||||
|
||||
pub mod logging;
|
||||
pub mod rfc6052;
|
||||
pub mod rfc6052;
|
||||
pub mod packet_handler;
|
79
src/common/packet_handler.rs
Normal file
79
src/common/packet_handler.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
pub fn handle_packet<Ipv4Handler, Ipv6Handler>(
|
||||
packet: &[u8],
|
||||
ipv4_handler: Ipv4Handler,
|
||||
ipv6_handler: Ipv6Handler,
|
||||
) -> Option<Vec<u8>>
|
||||
where
|
||||
Ipv4Handler: Fn(&[u8], &Ipv4Addr, &Ipv4Addr) -> Result<Vec<u8>, interproto::error::Error>,
|
||||
Ipv6Handler: Fn(&[u8], &Ipv6Addr, &Ipv6Addr) -> Result<Vec<u8>, interproto::error::Error>,
|
||||
{
|
||||
// If the packet is empty, return nothing
|
||||
if packet.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Switch on the layer 3 protocol number to call the correct handler
|
||||
let layer_3_proto = packet[0] >> 4;
|
||||
log::trace!("New packet with layer 3 protocol: {}", layer_3_proto);
|
||||
let handler_response = match layer_3_proto {
|
||||
// IPv4
|
||||
4 => {
|
||||
// Extract the source and destination addresses
|
||||
let source_addr =
|
||||
Ipv4Addr::from(u32::from_be_bytes(packet[12..16].try_into().unwrap()));
|
||||
let destination_addr =
|
||||
Ipv4Addr::from(u32::from_be_bytes(packet[16..20].try_into().unwrap()));
|
||||
|
||||
// Call the handler
|
||||
ipv4_handler(packet, &source_addr, &destination_addr)
|
||||
}
|
||||
|
||||
// IPv6
|
||||
6 => {
|
||||
// Extract the source and destination addresses
|
||||
let source_addr =
|
||||
Ipv6Addr::from(u128::from_be_bytes(packet[8..24].try_into().unwrap()));
|
||||
let destination_addr =
|
||||
Ipv6Addr::from(u128::from_be_bytes(packet[24..40].try_into().unwrap()));
|
||||
|
||||
// Call the handler
|
||||
ipv6_handler(packet, &source_addr, &destination_addr)
|
||||
}
|
||||
|
||||
// Unknown protocol numbers can't be handled
|
||||
proto => {
|
||||
log::warn!("Unknown Layer 3 protocol: {}", proto);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// The response from the handler may or may not be a warn-able error
|
||||
match handler_response {
|
||||
// If we get data, return it
|
||||
Ok(data) => Some(data),
|
||||
// If we get an error, handle it and return None
|
||||
Err(error) => match error {
|
||||
interproto::error::Error::PacketTooShort { expected, actual } => {
|
||||
log::warn!(
|
||||
"Got packet with length {} when expecting at least {} bytes",
|
||||
actual,
|
||||
expected
|
||||
);
|
||||
None
|
||||
}
|
||||
interproto::error::Error::UnsupportedIcmpType(icmp_type) => {
|
||||
log::warn!("Got a packet with an unsupported ICMP type: {}", icmp_type);
|
||||
None
|
||||
}
|
||||
interproto::error::Error::UnsupportedIcmpv6Type(icmpv6_type) => {
|
||||
log::warn!(
|
||||
"Got a packet with an unsupported ICMPv6 type: {}",
|
||||
icmpv6_type
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
};
|
||||
|
||||
use crate::common::packet_handler::handle_packet;
|
||||
|
||||
mod common;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@ -24,6 +26,10 @@ struct Args {
|
||||
#[clap(long="via", default_value_t = ("64:ff9b::/96").parse().unwrap(), value_parser = parse_network_specific_prefix)]
|
||||
embed_prefix: Ipv6Net,
|
||||
|
||||
/// One or more customer-side IPv4 prefixes to allow through CLAT
|
||||
#[clap(short = 'c', long = "customer-prefix", required = true)]
|
||||
customer_pool: Vec<Ipv4Net>,
|
||||
|
||||
/// Explicitly set the interface name to use
|
||||
#[clap(short, long, default_value_t = ("clat%d").to_string())]
|
||||
interface: String,
|
||||
@ -52,23 +58,44 @@ pub async fn main() {
|
||||
let mut tun = Tun::new(&args.interface).unwrap();
|
||||
log::debug!("Created TUN interface: {}", tun.name());
|
||||
|
||||
// Configure the new interface
|
||||
// - Bring up
|
||||
// - Add IPv6 prefix as a route
|
||||
// - Point IPv4 default route to the new interface
|
||||
// Get the interface index
|
||||
let rt_handle = rtnl::new_handle().unwrap();
|
||||
let tun_link_idx = rtnl::link::get_link_index(&rt_handle, tun.name())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
// Bring the interface up
|
||||
rtnl::link::link_up(&rt_handle, tun_link_idx).await.unwrap();
|
||||
rtnl::route::route_add(IpNet::V6(args.embed_prefix), &rt_handle, tun_link_idx)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Add an IPv4 default route towards the interface
|
||||
rtnl::route::route_add(IpNet::V4(Ipv4Net::default()), &rt_handle, tun_link_idx)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Add an IPv6 route for each customer prefix
|
||||
for customer_prefix in args.customer_pool {
|
||||
let embedded_customer_prefix = unsafe {
|
||||
Ipv6Net::new(
|
||||
embed_ipv4_addr_unchecked(customer_prefix.addr(), args.embed_prefix),
|
||||
args.embed_prefix.prefix_len() + customer_prefix.prefix_len(),
|
||||
)
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
log::debug!(
|
||||
"Adding route for {} to {}",
|
||||
embedded_customer_prefix,
|
||||
tun.name()
|
||||
);
|
||||
rtnl::route::route_add(
|
||||
IpNet::V6(embedded_customer_prefix),
|
||||
&rt_handle,
|
||||
tun_link_idx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Translate all incoming packets
|
||||
log::info!("Translating packets on {}", tun.name());
|
||||
let mut buffer = vec![0u8; 1500];
|
||||
@ -77,66 +104,27 @@ pub async fn main() {
|
||||
let len = tun.read(&mut buffer).unwrap();
|
||||
|
||||
// Translate it based on the Layer 3 protocol number
|
||||
let layer_3_proto = buffer[0] >> 4;
|
||||
log::trace!("New packet with layer 3 protocol: {}", layer_3_proto);
|
||||
match match layer_3_proto {
|
||||
// IPv4
|
||||
4 => translate_ipv4_to_ipv6(
|
||||
&buffer[..len],
|
||||
unsafe {
|
||||
embed_ipv4_addr_unchecked(
|
||||
Ipv4Addr::from(u32::from_be_bytes(buffer[12..16].try_into().unwrap())),
|
||||
args.embed_prefix,
|
||||
)
|
||||
},
|
||||
unsafe {
|
||||
embed_ipv4_addr_unchecked(
|
||||
Ipv4Addr::from(u32::from_be_bytes(buffer[16..20].try_into().unwrap())),
|
||||
args.embed_prefix,
|
||||
)
|
||||
},
|
||||
),
|
||||
|
||||
// IPv6
|
||||
6 => translate_ipv6_to_ipv4(
|
||||
&buffer[..len],
|
||||
unsafe {
|
||||
extract_ipv4_addr_unchecked(
|
||||
Ipv6Addr::from(u128::from_be_bytes(buffer[8..24].try_into().unwrap())),
|
||||
args.embed_prefix.prefix_len(),
|
||||
)
|
||||
},
|
||||
unsafe {
|
||||
extract_ipv4_addr_unchecked(
|
||||
Ipv6Addr::from(u128::from_be_bytes(buffer[24..40].try_into().unwrap())),
|
||||
args.embed_prefix.prefix_len(),
|
||||
)
|
||||
},
|
||||
),
|
||||
// Unknown
|
||||
proto => {
|
||||
log::warn!("Unknown Layer 3 protocol: {}", proto);
|
||||
continue;
|
||||
}
|
||||
} {
|
||||
Ok(data) => {
|
||||
// Write the translated packet back to the TUN interface
|
||||
tun.write(&data).unwrap();
|
||||
}
|
||||
Err(error) => match error {
|
||||
interproto::error::Error::PacketTooShort { expected, actual } => log::warn!(
|
||||
"Got packet with length {} when expecting at least {} bytes",
|
||||
actual,
|
||||
expected
|
||||
),
|
||||
interproto::error::Error::UnsupportedIcmpType(icmp_type) => {
|
||||
log::warn!("Got a packet with an unsupported ICMP type: {}", icmp_type)
|
||||
}
|
||||
interproto::error::Error::UnsupportedIcmpv6Type(icmpv6_type) => log::warn!(
|
||||
"Got a packet with an unsupported ICMPv6 type: {}",
|
||||
icmpv6_type
|
||||
),
|
||||
if let Some(output) = handle_packet(
|
||||
&buffer[..len],
|
||||
// IPv4 -> IPv6
|
||||
|packet, source, dest| {
|
||||
translate_ipv4_to_ipv6(
|
||||
packet,
|
||||
unsafe { embed_ipv4_addr_unchecked(*source, args.embed_prefix) },
|
||||
unsafe { embed_ipv4_addr_unchecked(*dest, args.embed_prefix) },
|
||||
)
|
||||
},
|
||||
};
|
||||
// IPv6 -> IPv4
|
||||
|packet, source, dest| {
|
||||
translate_ipv6_to_ipv4(
|
||||
packet,
|
||||
unsafe { extract_ipv4_addr_unchecked(*source, args.embed_prefix.prefix_len()) },
|
||||
unsafe { extract_ipv4_addr_unchecked(*dest, args.embed_prefix.prefix_len()) },
|
||||
)
|
||||
},
|
||||
) {
|
||||
// Write the packet if we get one back from the handler functions
|
||||
tun.write(&output).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user