wip clat
This commit is contained in:
parent
032dcb7fa9
commit
833d4308fb
@ -41,7 +41,13 @@ exclude = ["/.github/", "/.vscode/"]
|
|||||||
# lazy_static = "1.4.0"
|
# lazy_static = "1.4.0"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["libs/easy-tun", "libs/fast-nat", "libs/interproto", "libs/rfc6052"]
|
members = [
|
||||||
|
"libs/easy-tun",
|
||||||
|
"libs/fast-nat",
|
||||||
|
"libs/interproto",
|
||||||
|
"libs/rfc6052",
|
||||||
|
"libs/rtnl",
|
||||||
|
]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "protomask"
|
name = "protomask"
|
||||||
@ -61,6 +67,7 @@ easy-tun = { path = "libs/easy-tun" }
|
|||||||
fast-nat = { path = "libs/fast-nat" }
|
fast-nat = { path = "libs/fast-nat" }
|
||||||
interproto = { path = "libs/interproto" }
|
interproto = { path = "libs/interproto" }
|
||||||
rfc6052 = { path = "libs/rfc6052" }
|
rfc6052 = { path = "libs/rfc6052" }
|
||||||
|
rtnl = { path = "libs/rtnl", features = ["tokio"] }
|
||||||
|
|
||||||
# External Dependencies
|
# External Dependencies
|
||||||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
|
||||||
|
@ -65,6 +65,7 @@ pub fn translate_ipv4_to_ipv6(
|
|||||||
ipv6_packet.set_hop_limit(ipv4_packet.get_ttl());
|
ipv6_packet.set_hop_limit(ipv4_packet.get_ttl());
|
||||||
ipv6_packet.set_source(new_source);
|
ipv6_packet.set_source(new_source);
|
||||||
ipv6_packet.set_destination(new_destination);
|
ipv6_packet.set_destination(new_destination);
|
||||||
|
ipv6_packet.set_payload_length(new_payload.len().try_into().unwrap());
|
||||||
|
|
||||||
// Copy the payload to the buffer
|
// Copy the payload to the buffer
|
||||||
ipv6_packet.set_payload(&new_payload);
|
ipv6_packet.set_payload(&new_payload);
|
||||||
@ -118,6 +119,7 @@ pub fn translate_ipv6_to_ipv4(
|
|||||||
|
|
||||||
// Set the header fields
|
// Set the header fields
|
||||||
ipv4_packet.set_version(4);
|
ipv4_packet.set_version(4);
|
||||||
|
ipv4_packet.set_header_length(5);
|
||||||
ipv4_packet.set_ttl(ipv6_packet.get_hop_limit());
|
ipv4_packet.set_ttl(ipv6_packet.get_hop_limit());
|
||||||
ipv4_packet.set_next_level_protocol(match ipv6_packet.get_next_header() {
|
ipv4_packet.set_next_level_protocol(match ipv6_packet.get_next_header() {
|
||||||
IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp,
|
IpNextHeaderProtocols::Icmpv6 => IpNextHeaderProtocols::Icmp,
|
||||||
@ -125,6 +127,7 @@ pub fn translate_ipv6_to_ipv4(
|
|||||||
});
|
});
|
||||||
ipv4_packet.set_source(new_source);
|
ipv4_packet.set_source(new_source);
|
||||||
ipv4_packet.set_destination(new_destination);
|
ipv4_packet.set_destination(new_destination);
|
||||||
|
ipv4_packet.set_total_length((Ipv4Packet::minimum_packet_size() + new_payload.len()).try_into().unwrap());
|
||||||
|
|
||||||
// Copy the payload to the buffer
|
// Copy the payload to the buffer
|
||||||
ipv4_packet.set_payload(&new_payload);
|
ipv4_packet.set_payload(&new_payload);
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rfc6052"
|
name = "rtnl"
|
||||||
version = "1.0.0"
|
version = "0.1.0"
|
||||||
authors = ["Evan Pratten <ewpratten@gmail.com>"]
|
authors = ["Evan Pratten <ewpratten@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Rust functions for interacting with RFC6052 IPv4-Embedded IPv6 Addresses"
|
description = "Slightly sane wrapper around rtnetlink"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://github.com/ewpratten/protomask/tree/master/libs/rfc6052"
|
homepage = "https://github.com/ewpratten/protomask/tree/master/libs/rtnl"
|
||||||
documentation = "https://docs.rs/rfc6052"
|
documentation = "https://docs.rs/rtnl"
|
||||||
repository = "https://github.com/ewpratten/protomask"
|
repository = "https://github.com/ewpratten/protomask"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
keywords = []
|
keywords = []
|
||||||
categories = []
|
categories = []
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "^1.0.44"
|
tokio = { version = "1.29.1", optional = true, features = ["rt-multi-thread"] }
|
||||||
|
log = "0.4.19"
|
||||||
|
rtnetlink = "0.13.1"
|
||||||
|
futures = "0.3.28"
|
||||||
ipnet = "^2.8.0"
|
ipnet = "^2.8.0"
|
3
libs/rtnl/README.md
Normal file
3
libs/rtnl/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# RTNL
|
||||||
|
|
||||||
|
A slightly sane wrapper around [`rtnetlink`](https://crates.io/crates/rtnetlink)
|
71
libs/rtnl/src/ip.rs
Normal file
71
libs/rtnl/src/ip.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//! Utilities for manipulating the addresses assigned to links
|
||||||
|
|
||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
use rtnetlink::Handle;
|
||||||
|
|
||||||
|
/// Add an IP address to a link
|
||||||
|
pub async fn addr_add(
|
||||||
|
ip_addr: IpAddr,
|
||||||
|
prefix_len: u8,
|
||||||
|
rt_handle: &Handle,
|
||||||
|
link_index: u32,
|
||||||
|
) -> Result<(), rtnetlink::Error> {
|
||||||
|
log::trace!("Adding address {} to link {}", ip_addr, link_index);
|
||||||
|
rt_handle
|
||||||
|
.address()
|
||||||
|
.add(link_index, ip_addr, prefix_len)
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!("Failed to add address {} to link {}", ip_addr, link_index);
|
||||||
|
log::error!("{}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an IP address from a link
|
||||||
|
pub async fn addr_del(
|
||||||
|
ip_addr: IpAddr,
|
||||||
|
prefix_len: u8,
|
||||||
|
rt_handle: &Handle,
|
||||||
|
link_index: u32,
|
||||||
|
) -> Result<(), rtnetlink::Error> {
|
||||||
|
log::trace!("Removing address {} from link {}", ip_addr, link_index);
|
||||||
|
|
||||||
|
// Find the address message that matches the given address
|
||||||
|
if let Some(address_message) = rt_handle
|
||||||
|
.address()
|
||||||
|
.get()
|
||||||
|
.set_link_index_filter(link_index)
|
||||||
|
.set_address_filter(ip_addr)
|
||||||
|
.set_prefix_length_filter(prefix_len)
|
||||||
|
.execute()
|
||||||
|
.try_next()
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!("Failed to find address {} on link {}", ip_addr, link_index);
|
||||||
|
log::error!("{}", err);
|
||||||
|
err
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
// Delete the address
|
||||||
|
rt_handle
|
||||||
|
.address()
|
||||||
|
.del(address_message)
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!(
|
||||||
|
"Failed to remove address {} from link {}",
|
||||||
|
ip_addr,
|
||||||
|
link_index
|
||||||
|
);
|
||||||
|
log::error!("{}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
22
libs/rtnl/src/lib.rs
Normal file
22
libs/rtnl/src/lib.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![deny(clippy::pedantic)]
|
||||||
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
#![allow(clippy::missing_errors_doc)]
|
||||||
|
#![allow(clippy::missing_panics_doc)]
|
||||||
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
|
pub mod link;
|
||||||
|
pub mod ip;
|
||||||
|
pub mod route;
|
||||||
|
|
||||||
|
/// Get a handle on a new rtnetlink connection
|
||||||
|
#[cfg(feature="tokio")]
|
||||||
|
pub fn new_handle() -> Result<rtnetlink::Handle, std::io::Error> {
|
||||||
|
let (rt_connection, rt_handle, _) = rtnetlink::new_connection().map_err(|err| {
|
||||||
|
log::error!("Failed to open rtnetlink connection");
|
||||||
|
log::error!("{}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
tokio::spawn(rt_connection);
|
||||||
|
Ok(rt_handle)
|
||||||
|
}
|
31
libs/rtnl/src/link.rs
Normal file
31
libs/rtnl/src/link.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//! Utilities for operating on a link/interface/device
|
||||||
|
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
use rtnetlink::Handle;
|
||||||
|
|
||||||
|
/// Bring up a link by its link index
|
||||||
|
pub async fn link_up(rt_handle: &Handle, link_index: u32) -> Result<(), rtnetlink::Error> {
|
||||||
|
log::trace!("Bringing up link {}", link_index);
|
||||||
|
rt_handle.link().set(link_index).up().execute().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bring down a link by its link index
|
||||||
|
pub async fn link_down(rt_handle: &Handle, link_index: u32) -> Result<(), rtnetlink::Error> {
|
||||||
|
log::trace!("Bringing down link {}", link_index);
|
||||||
|
rt_handle.link().set(link_index).down().execute().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the link index of a link by its name
|
||||||
|
pub async fn get_link_index(
|
||||||
|
rt_handle: &Handle,
|
||||||
|
link_name: &str,
|
||||||
|
) -> Result<Option<u32>, rtnetlink::Error> {
|
||||||
|
Ok(rt_handle
|
||||||
|
.link()
|
||||||
|
.get()
|
||||||
|
.match_name(link_name.to_owned())
|
||||||
|
.execute()
|
||||||
|
.try_next()
|
||||||
|
.await?
|
||||||
|
.map(|message| message.header.index))
|
||||||
|
}
|
41
libs/rtnl/src/route.rs
Normal file
41
libs/rtnl/src/route.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//! Utilities for interacting with the routing table
|
||||||
|
|
||||||
|
use ipnet::IpNet;
|
||||||
|
use rtnetlink::Handle;
|
||||||
|
|
||||||
|
/// Add a route to a link
|
||||||
|
pub async fn route_add(
|
||||||
|
destination: IpNet,
|
||||||
|
rt_handle: &Handle,
|
||||||
|
link_index: u32,
|
||||||
|
) -> Result<(), rtnetlink::Error> {
|
||||||
|
log::trace!("Adding route {} to link {}", destination, link_index);
|
||||||
|
match destination {
|
||||||
|
IpNet::V4(destination) => rt_handle
|
||||||
|
.route()
|
||||||
|
.add()
|
||||||
|
.v4()
|
||||||
|
.output_interface(link_index)
|
||||||
|
.destination_prefix(destination.addr(), destination.prefix_len())
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!("Failed to add route {} to link", destination);
|
||||||
|
log::error!("{}", err);
|
||||||
|
err
|
||||||
|
}),
|
||||||
|
IpNet::V6(destination) => rt_handle
|
||||||
|
.route()
|
||||||
|
.add()
|
||||||
|
.v6()
|
||||||
|
.output_interface(link_index)
|
||||||
|
.destination_prefix(destination.addr(), destination.prefix_len())
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!("Failed to add route {} to link", destination);
|
||||||
|
log::error!("{}", err);
|
||||||
|
err
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ use clap::Parser;
|
|||||||
use common::{logging::enable_logger, rfc6052::parse_network_specific_prefix};
|
use common::{logging::enable_logger, rfc6052::parse_network_specific_prefix};
|
||||||
use easy_tun::Tun;
|
use easy_tun::Tun;
|
||||||
use interproto::protocols::ip::{translate_ipv4_to_ipv6, translate_ipv6_to_ipv4};
|
use interproto::protocols::ip::{translate_ipv4_to_ipv6, translate_ipv6_to_ipv4};
|
||||||
use ipnet::Ipv6Net;
|
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||||
use nix::unistd::Uid;
|
use nix::unistd::Uid;
|
||||||
use rfc6052::{embed_ipv4_addr_unchecked, extract_ipv4_addr_unchecked};
|
use rfc6052::{embed_ipv4_addr_unchecked, extract_ipv4_addr_unchecked};
|
||||||
use std::{
|
use std::{
|
||||||
@ -48,7 +48,26 @@ pub async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bring up a TUN interface
|
// Bring up a TUN interface
|
||||||
|
log::debug!("Creating new TUN interface");
|
||||||
let mut tun = Tun::new(&args.interface).unwrap();
|
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
|
||||||
|
let rt_handle = rtnl::new_handle().unwrap();
|
||||||
|
let tun_link_idx = rtnl::link::get_link_index(&rt_handle, tun.name())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
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();
|
||||||
|
rtnl::route::route_add(IpNet::V4(Ipv4Net::default()), &rt_handle, tun_link_idx)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Translate all incoming packets
|
// Translate all incoming packets
|
||||||
log::info!("Translating packets on {}", tun.name());
|
log::info!("Translating packets on {}", tun.name());
|
||||||
@ -60,7 +79,7 @@ pub async fn main() {
|
|||||||
// Translate it based on the Layer 3 protocol number
|
// Translate it based on the Layer 3 protocol number
|
||||||
let layer_3_proto = buffer[0] >> 4;
|
let layer_3_proto = buffer[0] >> 4;
|
||||||
log::trace!("New packet with layer 3 protocol: {}", layer_3_proto);
|
log::trace!("New packet with layer 3 protocol: {}", layer_3_proto);
|
||||||
let output = match layer_3_proto {
|
match match layer_3_proto {
|
||||||
// IPv4
|
// IPv4
|
||||||
4 => translate_ipv4_to_ipv6(
|
4 => translate_ipv4_to_ipv6(
|
||||||
&buffer[..len],
|
&buffer[..len],
|
||||||
@ -99,10 +118,25 @@ pub async fn main() {
|
|||||||
log::warn!("Unknown Layer 3 protocol: {}", proto);
|
log::warn!("Unknown Layer 3 protocol: {}", proto);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
} {
|
||||||
.unwrap();
|
Ok(data) => {
|
||||||
|
|
||||||
// Write the translated packet back to the TUN interface
|
// Write the translated packet back to the TUN interface
|
||||||
tun.write(&output).unwrap();
|
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
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user