aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSascha Grunert <mail@saschagrunert.de>2017-02-21 12:36:37 +0100
committerSascha Grunert <mail@saschagrunert.de>2017-02-21 12:36:37 +0100
commit4e391f657f144d98cc47dc1c84ee8a8563a53fb4 (patch)
tree813d6b281f9cfe018a82d752fcce6a8e57202c4a /src
parentAdded README.md (diff)
downloadwireguard-rs-4e391f657f144d98cc47dc1c84ee8a8563a53fb4.tar.xz
wireguard-rs-4e391f657f144d98cc47dc1c84ee8a8563a53fb4.zip
Added initial implementation
- 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 server
Diffstat (limited to 'src')
-rw-r--r--src/bindgen.rs33
-rw-r--r--src/device.rs81
-rw-r--r--src/error.rs80
-rw-r--r--src/lib.rs88
4 files changed, 282 insertions, 0 deletions
diff --git a/src/bindgen.rs b/src/bindgen.rs
new file mode 100644
index 0000000..212e4ac
--- /dev/null
+++ b/src/bindgen.rs
@@ -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],
+ },
+ }
+ }
+}
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 0000000..5b5dfff
--- /dev/null
+++ b/src/device.rs
@@ -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()?)
+ }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..a6c9770
--- /dev/null
+++ b/src/error.rs
@@ -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)*)))
+ )
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..6efd5b2
--- /dev/null
+++ b/src/lib.rs
@@ -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)));
+ }
+ }
+}