mirror of https://git.zx2c4.com/wireguard-rs
Browse Source
- Using tokio.rs for async I/O handling - Using bindgen for ioctl and c structure handling - Added basic device handling with dummy support - Added error handling - Added simple unit test for testing the serversg/master
8 changed files with 366 additions and 0 deletions
@ -0,0 +1,17 @@
|
||||
# Generated by Cargo |
||||
# will have compiled files and executables |
||||
/target/ |
||||
Cargo.lock |
||||
|
||||
# swap |
||||
[._]*.s[a-w][a-z] |
||||
[._]s[a-w][a-z] |
||||
# session |
||||
Session.vim |
||||
# temporary |
||||
.netrwhist |
||||
*~ |
||||
# auto-generated tag files |
||||
tags |
||||
# backup files from cargo fmt |
||||
*.bk |
||||
@ -0,0 +1,12 @@
|
||||
[package] |
||||
name = "wireguard" |
||||
version = "0.1.0" |
||||
authors = ["The WireGuard developers", "Sascha Grunert <mail@saschagrunert.de>"] |
||||
build = "build.rs" |
||||
|
||||
[build-dependencies] |
||||
bindgen = "0" |
||||
|
||||
[dependencies] |
||||
futures = "0.1" |
||||
tokio-core = "0.1" |
||||
@ -0,0 +1,39 @@
|
||||
extern crate bindgen; |
||||
|
||||
use bindgen::Builder; |
||||
|
||||
use std::env; |
||||
use std::fs::File; |
||||
use std::io::Write; |
||||
use std::error::Error; |
||||
use std::path::PathBuf; |
||||
|
||||
static HEADERS: &[&str] = &["net/if.h", "linux/if_tun.h", "sys/ioctl.h"]; |
||||
|
||||
fn main() { |
||||
run().expect("Could not execute build script."); |
||||
} |
||||
|
||||
fn run() -> Result<(), Box<Error>> { |
||||
// Create a wrapper header file
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR")?); |
||||
let wrapper_path = out_path.join("wrapper.h"); |
||||
let mut wrapper = File::create(&wrapper_path)?; |
||||
for header in HEADERS { |
||||
writeln!(wrapper, "#include <{}>", header)?; |
||||
} |
||||
|
||||
// Generate the bindungs
|
||||
let wrapper_path_str = wrapper_path.to_str().expect("Wrapper include path corrupt."); |
||||
let bindings = Builder::default() |
||||
.no_unstable_rust() |
||||
.generate_comments(true) |
||||
.hide_type("pthread_mutex_t") |
||||
.header(wrapper_path_str) |
||||
.generate() |
||||
.expect("Unable to generate bindings"); |
||||
|
||||
// Write the bindungs to the output directory
|
||||
bindings.write_to_file(out_path.join("bindings.rs"))?; |
||||
Ok(()) |
||||
} |
||||
@ -0,0 +1,33 @@
|
||||
//! Bindgen source code
|
||||
#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case, dead_code)] |
||||
|
||||
pub const TUNSETIFF: u64 = (1 << 0 + 8 + 8 + 14) | (84 << 0 + 8) | (202 << 0) | (4 << 0 + 8 + 8); |
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs")); |
||||
|
||||
impl ifreq { |
||||
/// Create a new `ifreq`
|
||||
pub fn new() -> Self { |
||||
ifreq { |
||||
ifr_ifrn: ifreq__bindgen_ty_1 { |
||||
ifrn_name: __BindgenUnionField::new(), |
||||
bindgen_union_field: [0; IFNAMSIZ as usize], |
||||
}, |
||||
ifr_ifru: ifreq__bindgen_ty_2 { |
||||
ifru_addr: __BindgenUnionField::new(), |
||||
ifru_dstaddr: __BindgenUnionField::new(), |
||||
ifru_broadaddr: __BindgenUnionField::new(), |
||||
ifru_netmask: __BindgenUnionField::new(), |
||||
ifru_hwaddr: __BindgenUnionField::new(), |
||||
ifru_flags: __BindgenUnionField::new(), |
||||
ifru_ivalue: __BindgenUnionField::new(), |
||||
ifru_mtu: __BindgenUnionField::new(), |
||||
ifru_map: __BindgenUnionField::new(), |
||||
ifru_slave: __BindgenUnionField::new(), |
||||
ifru_newname: __BindgenUnionField::new(), |
||||
ifru_data: __BindgenUnionField::new(), |
||||
bindgen_union_field: [0u64; 3usize], |
||||
}, |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,81 @@
|
||||
//! Tunnel device handling
|
||||
use std::path::PathBuf; |
||||
use std::io::{Read, Write}; |
||||
use std::fs::{File, OpenOptions}; |
||||
use std::os::unix::io::AsRawFd; |
||||
|
||||
use bindgen::*; |
||||
use error::WgResult; |
||||
|
||||
#[derive(Debug)] |
||||
/// A certain device
|
||||
pub struct Device { |
||||
/// The interface name
|
||||
pub name: String, |
||||
|
||||
/// The tunnel device file descriptor
|
||||
pub fd: File, |
||||
} |
||||
|
||||
impl Device { |
||||
/// Create a new tunneling `Device`
|
||||
pub fn new(name: &str) -> WgResult<Self> { |
||||
// Get a file descriptor to the operating system
|
||||
let fd = OpenOptions::new().read(true).write(true).open("/dev/net/tun")?; |
||||
|
||||
// Get the default interface options
|
||||
let mut ifr = ifreq::new(); |
||||
|
||||
{ |
||||
// Set the interface name
|
||||
let ifr_name = unsafe { ifr.ifr_ifrn.ifrn_name.as_mut() }; |
||||
for (index, character) in name.as_bytes().iter().enumerate() { |
||||
if index >= IFNAMSIZ as usize - 1 { |
||||
bail!("Interface name too long."); |
||||
} |
||||
ifr_name[index] = *character as i8; |
||||
} |
||||
|
||||
// Set the interface flags
|
||||
let ifr_flags = unsafe { ifr.ifr_ifru.ifru_flags.as_mut() }; |
||||
*ifr_flags = (IFF_TUN | IFF_NO_PI) as i16; |
||||
} |
||||
|
||||
// Create the tunnel device
|
||||
if unsafe { ioctl(fd.as_raw_fd(), TUNSETIFF, &ifr) < 0 } { |
||||
bail!("Device creation failed."); |
||||
} |
||||
|
||||
Ok(Device { |
||||
name: name.to_owned(), |
||||
fd: fd, |
||||
}) |
||||
} |
||||
|
||||
/// Create a dummy device for testing
|
||||
pub fn dummy(name: &str) -> WgResult<Self> { |
||||
let fd = OpenOptions::new().read(true) |
||||
.write(true) |
||||
.create(true) |
||||
.open(PathBuf::from("/tmp").join(name))?; |
||||
Ok(Device { |
||||
name: name.to_owned(), |
||||
fd: fd, |
||||
}) |
||||
} |
||||
|
||||
/// Reads a frame from the device, returns the number of bytes read
|
||||
pub fn read(&mut self, mut buffer: &mut [u8]) -> WgResult<usize> { |
||||
Ok(self.fd.read(&mut buffer)?) |
||||
} |
||||
|
||||
/// Write a frame to the device
|
||||
pub fn write(&mut self, data: &[u8]) -> WgResult<usize> { |
||||
Ok(self.fd.write(data)?) |
||||
} |
||||
|
||||
/// Flush the device
|
||||
pub fn flush(&mut self) -> WgResult<()> { |
||||
Ok(self.fd.flush()?) |
||||
} |
||||
} |
||||
@ -0,0 +1,80 @@
|
||||
//! Everything related to error handling
|
||||
use std::error::Error; |
||||
use std::{fmt, io, net, convert}; |
||||
|
||||
/// Common Tunnel Result type
|
||||
pub type WgResult<T> = Result<T, WgError>; |
||||
|
||||
/// The global Error type for wiki
|
||||
pub struct WgError { |
||||
/// A further description for the error
|
||||
pub description: String, |
||||
|
||||
/// The cause for this error
|
||||
pub cause: Option<Box<Error>>, |
||||
} |
||||
|
||||
/// Representation of an error case
|
||||
impl WgError { |
||||
/// Creates a new `WgError`
|
||||
pub fn new(description: &str) -> Self { |
||||
WgError { |
||||
description: description.to_string(), |
||||
cause: None, |
||||
} |
||||
} |
||||
|
||||
/// Returns the corresponding `io::ErrorKind` for this error
|
||||
pub fn kind(&self) -> io::ErrorKind { |
||||
io::ErrorKind::Other |
||||
} |
||||
} |
||||
|
||||
impl fmt::Display for WgError { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
write!(f, "{}", self.description) |
||||
} |
||||
} |
||||
|
||||
impl fmt::Debug for WgError { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
fmt::Display::fmt(self, f) |
||||
} |
||||
} |
||||
|
||||
impl convert::From<WgError> for io::Error { |
||||
fn from(tunnel_error: WgError) -> Self { |
||||
io::Error::new(io::ErrorKind::Other, tunnel_error.description) |
||||
} |
||||
} |
||||
|
||||
impl Error for WgError { |
||||
fn description(&self) -> &str { |
||||
&self.description |
||||
} |
||||
} |
||||
|
||||
macro_rules! from_error { |
||||
($($p:ty,)*) => ( |
||||
$(impl From<$p> for WgError { |
||||
fn from(err: $p) -> Self { |
||||
WgError { |
||||
description: err.description().to_owned(), |
||||
cause: Some(Box::new(err)), |
||||
} |
||||
} |
||||
})* |
||||
) |
||||
} |
||||
|
||||
from_error! { |
||||
io::Error, |
||||
net::AddrParseError, |
||||
} |
||||
|
||||
macro_rules! bail { |
||||
($($fmt:tt)*) => ( |
||||
#[cfg_attr(feature = "cargo-clippy", allow(useless_format))] |
||||
return Err(::error::WgError::new(&format!($($fmt)*))) |
||||
) |
||||
} |
||||
@ -0,0 +1,88 @@
|
||||
//! The WireGuard implementation in Rust
|
||||
|
||||
#[macro_use] |
||||
extern crate tokio_core; |
||||
extern crate futures; |
||||
|
||||
#[macro_use] |
||||
pub mod error; |
||||
pub mod device; |
||||
mod bindgen; |
||||
|
||||
use device::Device; |
||||
use error::WgResult; |
||||
|
||||
use std::io; |
||||
use std::net::SocketAddr; |
||||
|
||||
use futures::{Future, Poll}; |
||||
use tokio_core::net::UdpSocket; |
||||
use tokio_core::reactor::Handle; |
||||
|
||||
/// The main tunnel structure
|
||||
pub struct Wireguard { |
||||
/// A tunneling device
|
||||
device: Device, |
||||
|
||||
/// The VPN server socket
|
||||
server: UdpSocket, |
||||
|
||||
/// An internal packet buffer
|
||||
buffer: Vec<u8>, |
||||
|
||||
/// Things to send
|
||||
to_send: Option<(usize, SocketAddr)>, |
||||
} |
||||
|
||||
impl Wireguard { |
||||
/// Creates a new `Wireguard` instance
|
||||
pub fn new(handle: &Handle) -> WgResult<Self> { |
||||
// Create a tunneling device
|
||||
let device = Device::dummy("wg")?; |
||||
|
||||
// Create a server for the tunnel
|
||||
let addr = "127.0.0.1:8080".to_owned().parse()?; |
||||
let server = UdpSocket::bind(&addr, handle)?; |
||||
|
||||
Ok(Wireguard { |
||||
device: device, |
||||
server: server, |
||||
buffer: vec![0; 1500], |
||||
to_send: None, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl Future for Wireguard { |
||||
type Item = (); |
||||
type Error = io::Error; |
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> { |
||||
loop { |
||||
// Check if a message needs to be processed
|
||||
if let Some((size, peer)) = self.to_send { |
||||
// Write the message to the tunnel device
|
||||
let send_bytes = try_nb!(self.device.write(&self.buffer[..size])); |
||||
|
||||
// Set `to_send` to `None` if done
|
||||
self.to_send = None; |
||||
println!("Wrote {}/{} bytes from {} to tunnel device", |
||||
send_bytes, |
||||
size, |
||||
peer); |
||||
|
||||
|
||||
// Read from the tunnel device and write to the client
|
||||
// let read_bytes = try_nb!(self.device.read(&mut self.buffer));
|
||||
// try_nb!(self.server.send_to(&self.buffer[..read_bytes], &peer));
|
||||
// println!("Read {} bytes from tunnel device", read_bytes);
|
||||
} |
||||
|
||||
// Flush the device file descriptor
|
||||
try_nb!(self.device.flush()); |
||||
|
||||
// If `to_send` is `None`, we can receive the next message from the client
|
||||
self.to_send = Some(try_nb!(self.server.recv_from(&mut self.buffer))); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,16 @@
|
||||
extern crate wireguard; |
||||
extern crate tokio_core; |
||||
|
||||
use tokio_core::reactor::Core; |
||||
use wireguard::Wireguard; |
||||
|
||||
#[test] |
||||
fn server() { |
||||
// Setup tokio
|
||||
let mut core = Core::new().unwrap(); |
||||
let handle = core.handle(); |
||||
|
||||
// Run the core with the tunnel
|
||||
let tunnel = Wireguard::new(&handle).unwrap(); |
||||
core.run(tunnel).unwrap(); |
||||
} |
||||
Loading…
Reference in new issue