diff --git a/src/cloud.rs b/src/cloud.rs index 2d98925..ca331c7 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -10,8 +10,10 @@ use std::marker::PhantomData; use time::{Duration, SteadyTime, precise_time_ns}; use epoll; +use super::device::{TunDevice, TapDevice}; use super::udpmessage::{encode, decode, Options, Message}; -use super::ethernet::{Frame, EthAddr, TapDevice, MacTable}; +use super::ethernet::{Frame, EthAddr, MacTable}; +use super::ip::{InternetProtocol, IpAddress, RoutingTable}; pub type NetworkId = u64; @@ -324,3 +326,17 @@ impl TapCloud { Self::new(device, listen, network_id, table, peer_timeout) } } + + +pub type TunCloud = GenericCloud; + +impl TunCloud { + pub fn new_tun_cloud(device: &str, listen: String, network_id: Option, table: RoutingTable, peer_timeout: Duration) -> Self { + let device = match TunDevice::new(device) { + Ok(device) => device, + _ => panic!("Failed to open tun device") + }; + info!("Opened tun device {}", device.ifname()); + Self::new(device, listen, network_id, table, peer_timeout) + } +} diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 0000000..c5be829 --- /dev/null +++ b/src/device.rs @@ -0,0 +1,91 @@ +use std::os::unix::io::{AsRawFd, RawFd}; +use std::io::{Result as IoResult, Error as IoError, Read, Write}; +use std::marker::PhantomData; +use std::fs; + +use super::cloud::{Error, VirtualInterface}; + +extern { + fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32; + fn setup_tun_device(fd: i32, ifname: *mut u8) -> i32; +} + + +trait DeviceSetup { + fn setup_device(RawFd, &str) -> IoResult; +} + +struct TapSetup; + +impl DeviceSetup for TapSetup { + fn setup_device(fd: RawFd, ifname: &str) -> IoResult { + let mut ifname_string = String::with_capacity(32); + ifname_string.push_str(ifname); + ifname_string.push('\0'); + let mut ifname_c = ifname_string.into_bytes(); + let res = unsafe { setup_tap_device(fd, ifname_c.as_mut_ptr()) }; + match res { + 0 => Ok(String::from_utf8(ifname_c).unwrap()), + _ => Err(IoError::last_os_error()) + } + } +} + + +struct TunSetup; + +impl DeviceSetup for TunSetup { + fn setup_device(fd: RawFd, ifname: &str) -> IoResult { + let mut ifname_string = String::with_capacity(32); + ifname_string.push_str(ifname); + ifname_string.push('\0'); + let mut ifname_c = ifname_string.into_bytes(); + let res = unsafe { setup_tun_device(fd, ifname_c.as_mut_ptr()) }; + match res { + 0 => Ok(String::from_utf8(ifname_c).unwrap()), + _ => Err(IoError::last_os_error()) + } + } +} + + +pub struct Device { + fd: fs::File, + ifname: String, + _dummy_t: PhantomData +} + +impl Device { + pub fn new(ifname: &str) -> IoResult { + let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun")); + let ifname = try!(T::setup_device(fd.as_raw_fd(), ifname)); + Ok(Device{fd: fd, ifname: ifname, _dummy_t: PhantomData}) + } + + #[inline(always)] + pub fn ifname(&self) -> &str { + &self.ifname + } +} + +impl AsRawFd for Device { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl VirtualInterface for Device { + fn read(&mut self, mut buffer: &mut [u8]) -> Result { + self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error")) + } + + fn write(&mut self, data: &[u8]) -> Result<(), Error> { + match self.fd.write_all(&data) { + Ok(_) => Ok(()), + Err(_) => Err(Error::TunTapDevError("Write error")) + } + } +} + +pub type TapDevice = Device; +pub type TunDevice = Device; diff --git a/src/ethernet.rs b/src/ethernet.rs index caaa150..89f2480 100644 --- a/src/ethernet.rs +++ b/src/ethernet.rs @@ -1,18 +1,14 @@ -use std::{mem, ptr, fmt, fs}; +use std::{mem, fmt, fs}; use std::net::SocketAddr; use std::collections::HashMap; use std::os::unix::io::{AsRawFd, RawFd}; use std::io::{Result as IoResult, Error as IoError, Read, Write}; use super::cloud::{Error, Table, Protocol, VirtualInterface}; -use super::util::{as_bytes, as_obj}; +use super::util::as_obj; use time::{Duration, SteadyTime}; -extern { - fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32; -} - #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Mac(pub [u8; 6]); @@ -61,51 +57,6 @@ impl Protocol for Frame { } -pub struct TapDevice { - fd: fs::File, - ifname: String -} - -impl TapDevice { - pub fn new(ifname: &str) -> IoResult { - let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun")); - let mut ifname_string = String::with_capacity(32); - ifname_string.push_str(ifname); - ifname_string.push('\0'); - let mut ifname_c = ifname_string.into_bytes(); - let res = unsafe { setup_tap_device(fd.as_raw_fd(), ifname_c.as_mut_ptr()) }; - match res { - 0 => Ok(TapDevice{fd: fd, ifname: String::from_utf8(ifname_c).unwrap()}), - _ => Err(IoError::last_os_error()) - } - } - - #[inline(always)] - pub fn ifname(&self) -> &str { - &self.ifname - } -} - -impl AsRawFd for TapDevice { - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl VirtualInterface for TapDevice { - fn read(&mut self, mut buffer: &mut [u8]) -> Result { - self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error")) - } - - fn write(&mut self, data: &[u8]) -> Result<(), Error> { - match self.fd.write_all(&data) { - Ok(_) => Ok(()), - Err(_) => Err(Error::TunTapDevError("Write error")) - } - } -} - - struct MacTableValue { address: SocketAddr, timeout: SteadyTime diff --git a/src/ip.rs b/src/ip.rs index 2ff7de2..94cb3ae 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -1,7 +1,8 @@ use std::net::{SocketAddr, Ipv4Addr, Ipv6Addr}; use std::collections::{hash_map, HashMap}; +use std::ptr; -use super::cloud::{Protocol, Error}; +use super::cloud::{Protocol, Error, Table}; use super::util::{as_obj, as_bytes}; @@ -17,7 +18,43 @@ impl Protocol for InternetProtocol { type Address = IpAddress; fn parse(data: &[u8]) -> Result<(IpAddress, IpAddress), Error> { - unimplemented!() + if data.len() < 1 { + return Err(Error::ParseError("Empty header")); + } + let version = data[0] >> 4; + match version { + 4 => { + if data.len() < 20 { + return Err(Error::ParseError("Truncated header")); + } + let src_data = unsafe { as_obj::<[u8; 4]>(&data[12..]) }; + let src = Ipv4Addr::new(src_data[0], src_data[1], src_data[2], src_data[3]); + let dst_data = unsafe { as_obj::<[u8; 4]>(&data[16..]) }; + let dst = Ipv4Addr::new(dst_data[0], dst_data[1], dst_data[2], dst_data[3]); + Ok((IpAddress::V4(src), IpAddress::V4(dst))) + }, + 6 => { + if data.len() < 40 { + return Err(Error::ParseError("Truncated header")); + } + let src_data = unsafe { as_obj::<[u16; 8]>(&data[8..]) }; + let src = Ipv6Addr::new( + u16::from_be(src_data[0]), u16::from_be(src_data[1]), + u16::from_be(src_data[2]), u16::from_be(src_data[3]), + u16::from_be(src_data[4]), u16::from_be(src_data[5]), + u16::from_be(src_data[6]), u16::from_be(src_data[7]), + ); + let dst_data = unsafe { as_obj::<[u16; 8]>(&data[24..]) }; + let dst = Ipv6Addr::new( + u16::from_be(dst_data[0]), u16::from_be(dst_data[1]), + u16::from_be(dst_data[2]), u16::from_be(dst_data[3]), + u16::from_be(dst_data[4]), u16::from_be(dst_data[5]), + u16::from_be(dst_data[6]), u16::from_be(dst_data[7]), + ); + Ok((IpAddress::V6(src), IpAddress::V6(dst))) + }, + _ => Err(Error::ParseError("Invalid version")) + } } } @@ -46,7 +83,7 @@ impl RoutingTable { } } - pub fn lookup(&self, bytes: Vec) -> Option { + pub fn lookup_bytes(&self, bytes: &[u8]) -> Option { let mut len = bytes.len()/2 * 2; for i in 0..len/2 { if let Some(group) = self.0.get(&bytes[0..len-2*i]) { @@ -73,3 +110,34 @@ impl RoutingTable { None } } + +impl Table for RoutingTable { + type Address = IpAddress; + + fn learn(&mut self, _src: Self::Address, _addr: SocketAddr) { + //nothing to do + } + + fn lookup(&self, dst: &Self::Address) -> Option { + match dst { + &IpAddress::V4(addr) => { + let mut bytes = [0u8; 4]; + let ip = addr.octets(); + unsafe { ptr::copy_nonoverlapping(ip.as_ptr(), bytes.as_mut_ptr(), ip.len()) }; + self.lookup_bytes(&bytes[..]) + }, + &IpAddress::V6(addr) => { + let mut segments = addr.segments(); + for i in 0..8 { + segments[i] = segments[i].to_be(); + } + let bytes = unsafe { as_bytes(&segments) }; + self.lookup_bytes(bytes) + } + } + } + + fn housekeep(&mut self) { + //nothin to do + } +} diff --git a/src/main.rs b/src/main.rs index 9c2ede8..51f78af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ mod udpmessage; mod ethernet; mod ip; mod cloud; +mod device; use time::Duration; use docopt::Docopt;