Skip to content

Commit

Permalink
🚧 Routing!
Browse files Browse the repository at this point in the history
  • Loading branch information
malted committed Sep 16, 2023
1 parent e643d9d commit 3374254
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 2 deletions.
90 changes: 90 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions tun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ tokio = ["tokio/net", "dep:futures"]
[target.'cfg(feature = "tokio")'.dev-dependencies]
tokio = { features = ["rt", "macros"] }

[target.'cfg(linux)'.dependencies]
rtnetlink = "0.13.1"

[target.'cfg(windows)'.dependencies]
lazy_static = "1.4"
libloading = "0.7"
Expand Down
1 change: 1 addition & 0 deletions tun/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod imp;
pub(crate) mod imp;

mod options;
pub mod routing;

#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[cfg(feature = "tokio")]
Expand Down
148 changes: 148 additions & 0 deletions tun/src/routing.rs
Original file line number Diff line number Diff line change
@@ -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) })?;
}
23 changes: 21 additions & 2 deletions tun/src/unix/apple/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use byteorder::{ByteOrder, NetworkEndian};
use fehler::throws;
use fehler::{throw, throws};
use libc::{c_char, iovec, writev, AF_INET, AF_INET6};
use tracing::info;
use socket2::{Domain, SockAddr, Socket, Type};
Expand All @@ -9,6 +9,7 @@ use std::os::fd::{AsRawFd, RawFd};
use std::{io::Error, mem};
use tracing::instrument;


mod kern_control;
mod sys;

Expand Down Expand Up @@ -54,6 +55,11 @@ impl TunInterface {
#[throws]
#[instrument]
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 len = buf.len() as sys::socklen_t;
sys::syscall!(getsockopt(
Expand All @@ -63,7 +69,20 @@ impl TunInterface {
buf.as_mut_ptr() as *mut sys::c_void,
&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]
Expand Down
25 changes: 25 additions & 0 deletions tun/tests/routing.rs
Original file line number Diff line number Diff line change
@@ -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(())
}

0 comments on commit 3374254

Please sign in to comment.