Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
337425446b |
6 changed files with 288 additions and 2 deletions
90
Cargo.lock
generated
90
Cargo.lock
generated
|
|
@ -1034,6 +1034,71 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-packet-core"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"netlink-packet-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-packet-route"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"byteorder",
|
||||||
|
"libc",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-packet-utils"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"paste",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-proto"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "842c6770fc4bb33dd902f41829c61ef872b8e38de1405aa0b938b27b8fba12c3"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"log",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-sys",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-sys"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.26.2"
|
version = "0.26.2"
|
||||||
|
|
@ -1167,6 +1232,12 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
@ -1306,6 +1377,24 @@ dependencies = [
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtnetlink"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"log",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-route",
|
||||||
|
"netlink-packet-utils",
|
||||||
|
"netlink-proto",
|
||||||
|
"netlink-sys",
|
||||||
|
"nix",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
|
@ -1808,6 +1897,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rtnetlink",
|
||||||
"serde",
|
"serde",
|
||||||
"socket2",
|
"socket2",
|
||||||
"ssri",
|
"ssri",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ tokio = ["tokio/net", "dep:futures"]
|
||||||
[target.'cfg(feature = "tokio")'.dev-dependencies]
|
[target.'cfg(feature = "tokio")'.dev-dependencies]
|
||||||
tokio = { features = ["rt", "macros"] }
|
tokio = { features = ["rt", "macros"] }
|
||||||
|
|
||||||
|
[target.'cfg(linux)'.dependencies]
|
||||||
|
rtnetlink = "0.13.1"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
libloading = "0.7"
|
libloading = "0.7"
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ mod imp;
|
||||||
pub(crate) mod imp;
|
pub(crate) mod imp;
|
||||||
|
|
||||||
mod options;
|
mod options;
|
||||||
|
pub mod routing;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
|
|
|
||||||
148
tun/src/routing.rs
Normal file
148
tun/src/routing.rs
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
use std::{io, mem};
|
||||||
|
use crate::TunInterface;
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::process::Command;
|
||||||
|
use fehler::throws;
|
||||||
|
// use rtnetlink;
|
||||||
|
use std::io::{Error, Write};
|
||||||
|
use libc::{rt_msghdr, c_uchar, rt_metrics, sockaddr_dl};
|
||||||
|
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||||
|
use socket2::SockAddr;
|
||||||
|
|
||||||
|
struct Handle {
|
||||||
|
pub inner: socket2::Socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handle {
|
||||||
|
#[throws]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let inner = socket2::Socket::new(socket2::Domain::from(libc::PF_ROUTE), socket2::Type::RAW, None)?;
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Route<'a> {
|
||||||
|
pub destination: IpAddr, // Default: 0.0.0.0
|
||||||
|
pub destination_prefix: u8,
|
||||||
|
|
||||||
|
pub interface: &'a TunInterface,
|
||||||
|
|
||||||
|
// #[cfg(target_os = "linux")]
|
||||||
|
// pub ifindex: Option<u32>,
|
||||||
|
//
|
||||||
|
// #[cfg(target_os = "linux")]
|
||||||
|
// pub table: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Route<'a> {
|
||||||
|
#[throws]
|
||||||
|
pub fn new(destination: IpAddr, destination_prefix: u8, interface: &'a TunInterface) -> Self {
|
||||||
|
let mut handle = Handle::new()?;
|
||||||
|
|
||||||
|
let mut hdr: rt_msghdr = unsafe { mem::zeroed() };
|
||||||
|
hdr.rtm_version = libc::RTM_VERSION as c_uchar;
|
||||||
|
hdr.rtm_type = libc::RTM_ADD as c_uchar;
|
||||||
|
hdr.rtm_flags = libc::RTF_STATIC | libc::RTF_UP;
|
||||||
|
hdr.rtm_addrs = libc::RTA_DST | libc::RTA_NETMASK | libc::RTA_GATEWAY | libc::RTA_IFP;
|
||||||
|
hdr.rtm_seq = 1;
|
||||||
|
|
||||||
|
let destination_addr = SockAddr::from(SocketAddr::new(destination, 0));
|
||||||
|
let gateway_addr = SockAddr::from(SocketAddrV4::new(interface.ipv4_addr()?, 0));
|
||||||
|
|
||||||
|
let index = interface.index()?;
|
||||||
|
let (_, if_addr) = unsafe {
|
||||||
|
SockAddr::init(|storage, len| {
|
||||||
|
*len = mem::size_of::<libc::sockaddr_dl>() as u32;
|
||||||
|
|
||||||
|
let mut addr: &mut libc::sockaddr_dl = &mut *storage.cast();
|
||||||
|
addr.sdl_len = *len as u8;
|
||||||
|
addr.sdl_family = libc::AF_LINK as u8;
|
||||||
|
addr.sdl_index = index as u16;
|
||||||
|
Ok(())
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
let mask_addr = SockAddr::from(SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), 0));
|
||||||
|
|
||||||
|
hdr.rtm_msglen = (
|
||||||
|
(mem::size_of::<rt_msghdr>() as u32) + destination_addr.len() + gateway_addr.len() + if_addr.len() + mask_addr.len()
|
||||||
|
) as u16;
|
||||||
|
|
||||||
|
println!("0z {:#?}", if_addr.len());
|
||||||
|
let buffer = vec![];
|
||||||
|
let mut cursor = std::io::Cursor::new(buffer);
|
||||||
|
cursor.write_all(unsafe {
|
||||||
|
std::slice::from_raw_parts(&hdr as *const _ as *const _, mem::size_of::<rt_msghdr>())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
println!("one");
|
||||||
|
write_addr(&mut cursor, destination_addr)?;
|
||||||
|
println!("two");
|
||||||
|
write_addr(&mut cursor, gateway_addr)?;
|
||||||
|
println!("three");
|
||||||
|
write_addr(&mut cursor, if_addr)?;
|
||||||
|
println!("4");
|
||||||
|
write_addr(&mut cursor, mask_addr)?;
|
||||||
|
println!("five");
|
||||||
|
|
||||||
|
let buf = cursor.into_inner();
|
||||||
|
println!("cbuf len: {:#?}, calcsize: {:#?}", buf.len(), hdr.rtm_msglen);
|
||||||
|
|
||||||
|
handle.inner.write_all(&buf)?;
|
||||||
|
|
||||||
|
// Create handle
|
||||||
|
Self {
|
||||||
|
destination,
|
||||||
|
destination_prefix,
|
||||||
|
interface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn through_interface(&self, iface: TunInterface) {
|
||||||
|
// High-level overview:
|
||||||
|
// 1. Create a socket
|
||||||
|
iface.socket.bind(iface.addr);
|
||||||
|
|
||||||
|
// 2. Bind the socket to the interface
|
||||||
|
// 3. Add the route
|
||||||
|
// 4. Close the socket
|
||||||
|
// 5. Return the result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
#[cfg(target_platform = "linux")]
|
||||||
|
pub async fn add_route(route: Route) {
|
||||||
|
let conn = rtnetlink::new_connection()?;
|
||||||
|
|
||||||
|
let handle = conn.route();
|
||||||
|
let route_add_request = handle
|
||||||
|
.add()
|
||||||
|
.v4()
|
||||||
|
.destination_prefix(route.destination, route.destination_prefix)
|
||||||
|
.output_interface(route.interface.index()?)
|
||||||
|
.execute().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
#[cfg(target_vendor="apple")]
|
||||||
|
pub fn add_route(route: Route) {
|
||||||
|
// Construct
|
||||||
|
// Construct RT message
|
||||||
|
Command::new("route")
|
||||||
|
.arg("add")
|
||||||
|
.arg("-host")
|
||||||
|
.arg(route.destination.to_string())
|
||||||
|
.arg("-interface")
|
||||||
|
.arg(route.interface.name().expect("the interface's name"))
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute add route process");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn write_addr<T: Write>(sock: &mut T, addr: socket2::SockAddr) {
|
||||||
|
let len = addr.len() as usize;
|
||||||
|
let ptr = addr.as_ptr() as *const _;
|
||||||
|
sock.write_all(unsafe { std::slice::from_raw_parts(ptr, len) })?;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use byteorder::{ByteOrder, NetworkEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
use fehler::throws;
|
use fehler::{throw, throws};
|
||||||
use libc::{c_char, iovec, writev, AF_INET, AF_INET6};
|
use libc::{c_char, iovec, writev, AF_INET, AF_INET6};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use socket2::{Domain, SockAddr, Socket, Type};
|
use socket2::{Domain, SockAddr, Socket, Type};
|
||||||
|
|
@ -9,6 +9,7 @@ use std::os::fd::{AsRawFd, RawFd};
|
||||||
use std::{io::Error, mem};
|
use std::{io::Error, mem};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
|
|
||||||
mod kern_control;
|
mod kern_control;
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
|
|
@ -54,6 +55,11 @@ impl TunInterface {
|
||||||
#[throws]
|
#[throws]
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> String {
|
||||||
|
ifname_to_string(self.ifname()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn ifname(&self) -> [libc::c_char; libc::IFNAMSIZ] {
|
||||||
let mut buf = [0 as c_char; sys::IFNAMSIZ];
|
let mut buf = [0 as c_char; sys::IFNAMSIZ];
|
||||||
let mut len = buf.len() as sys::socklen_t;
|
let mut len = buf.len() as sys::socklen_t;
|
||||||
sys::syscall!(getsockopt(
|
sys::syscall!(getsockopt(
|
||||||
|
|
@ -63,7 +69,20 @@ impl TunInterface {
|
||||||
buf.as_mut_ptr() as *mut sys::c_void,
|
buf.as_mut_ptr() as *mut sys::c_void,
|
||||||
&mut len,
|
&mut len,
|
||||||
))?;
|
))?;
|
||||||
ifname_to_string(buf)
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
#[instrument]
|
||||||
|
pub fn index(&self) -> usize {
|
||||||
|
let ifname = self.ifname()?;
|
||||||
|
let index = unsafe { libc::if_nametoindex(ifname.as_ptr()) };
|
||||||
|
if index == 0 {
|
||||||
|
throw!(Error::last_os_error())
|
||||||
|
}
|
||||||
|
|
||||||
|
index as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
#[throws]
|
#[throws]
|
||||||
|
|
|
||||||
25
tun/tests/routing.rs
Normal file
25
tun/tests/routing.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use fehler::throws;
|
||||||
|
use std::io::Error;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
use tun::routing::{Route, add_route};
|
||||||
|
use tun::TunInterface;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[throws]
|
||||||
|
fn test_create() -> std::io::Result<()> {
|
||||||
|
let tun = TunInterface::new()?;
|
||||||
|
let name = tun.name()?;
|
||||||
|
println!("Interface name: {name}");
|
||||||
|
|
||||||
|
let addr = Ipv4Addr::new(10, 0, 0, 1);
|
||||||
|
tun.set_ipv4_addr(addr)?;
|
||||||
|
|
||||||
|
let dest = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8));
|
||||||
|
|
||||||
|
let route = Route::new(dest, 24, &tun)?;
|
||||||
|
add_route(route)?;
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue