Implement TAP interface support.

v0.7.x
whitequark 2016-12-10 23:15:26 +00:00
parent 77225b77a9
commit e7d6237279
8 changed files with 267 additions and 107 deletions

View File

@ -1,11 +1,13 @@
#![feature(range_contains, associated_consts)]
#![no_std]
#[cfg(test)]
extern crate byteorder;
#[cfg(any(test, feature = "std"))]
#[macro_use]
extern crate std;
extern crate byteorder;
#[cfg(feature = "std")]
extern crate libc;
pub mod phy;
pub mod wire;

View File

@ -2,13 +2,21 @@
//!
//! The `phy` module provides an interface for sending and receiving frames
//! through a physical (or perhaps virtualized) network device, [Device](trait.Device.html),
//! as well as some useful implementations of that trait.
//!
//! Currently the only implementation, [RawSocket](struct.RawSocket.html), is based on
//! Unix raw sockets, and only works on Linux.
//! as well as an implementations of that trait that uses the host OS,
//! [OsDevice](struct.OsDevice.html).
#[cfg(all(unix, feature = "std"))]
#[cfg(feature = "std")]
mod sys;
#[cfg(feature = "std")]
mod raw_socket;
#[cfg(all(feature = "std", target_os = "linux"))]
mod tap_interface;
#[cfg(feature = "std")]
pub use self::raw_socket::RawSocket;
#[cfg(all(feature = "std", target_os = "linux"))]
pub use self::tap_interface::TapInterface;
/// An interface for sending and receiving raw network frames.
///
@ -32,13 +40,10 @@ pub trait Device {
/// Transmits a frame.
///
/// It is expected that a `send` implementation would gain ownership of a buffer with
/// the requested size, provide it for emission, and then schedule it to be read from
/// the requested length, provide it for emission, and then schedule it to be read from
/// memory by the network device.
///
/// # Panics
/// This function may panic if `size` is larger than `MTU`.
fn send<F: FnOnce(&mut [u8])>(&mut self, size: usize, handler: F);
/// This function may panic if `len` is larger than `MTU`.
fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F);
}
#[cfg(all(unix, feature = "std"))]
pub use self::raw_socket::RawSocket;

View File

@ -1,112 +1,41 @@
extern crate std;
extern crate libc;
use std::{vec, io};
use super::{sys, Device};
use self::std::{mem, vec, io};
#[repr(C)]
struct ifreq {
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
}
const SIOCGIFMTU: libc::c_ulong = 0x8921;
const SIOCGIFINDEX: libc::c_ulong = 0x8933;
const ETH_P_ALL: libc::c_short = 0x0003;
/// A raw socket: a socket that captures the entire packet, up to and including
/// the link layer header.
/// A socket that captures or transmits the complete frame.
#[derive(Debug)]
pub struct RawSocket {
sockfd: libc::c_int,
lower: sys::RawSocketDesc,
buffer: vec::Vec<u8>
}
impl RawSocket {
/// Creates and returns a raw socket, bound to the interface called `name`.
/// Creates a raw socket, bound to the interface called `name`.
///
/// This requires superuser privileges or a corresponding capability bit
/// set on the executable.
pub fn new(name: &str) -> io::Result<RawSocket> {
unsafe {
let sockfd = libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ALL.to_be() as i32);
if sockfd == -1 {
return Err(io::Error::last_os_error())
}
let mut lower = try!(sys::RawSocketDesc::new(name));
try!(lower.bind_interface());
let mut ifreq = ifreq {
ifr_name: [0; libc::IF_NAMESIZE],
ifr_data: 0
};
for (i, byte) in name.as_bytes().iter().enumerate() {
ifreq.ifr_name[i] = *byte as libc::c_char
}
let res = libc::ioctl(sockfd, SIOCGIFINDEX, &mut ifreq as *mut ifreq);
if res == -1 {
libc::close(sockfd);
return Err(io::Error::last_os_error())
}
let if_index = ifreq.ifr_data;
let res = libc::ioctl(sockfd, SIOCGIFMTU, &mut ifreq as *mut ifreq);
if res == -1 {
libc::close(sockfd);
return Err(io::Error::last_os_error())
}
let if_mtu = ifreq.ifr_data;
let sockaddr = libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_protocol: ETH_P_ALL.to_be() as u16,
sll_ifindex: if_index as i32,
sll_hatype: 1,
sll_pkttype: 0,
sll_halen: 6,
sll_addr: [0; 8]
};
libc::bind(sockfd,
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
mem::size_of::<libc::sockaddr_ll>() as u32);
if res == -1 {
libc::close(sockfd);
return Err(io::Error::last_os_error())
}
let mut buffer = vec::Vec::new();
buffer.resize(if_mtu as usize, 0);
Ok(RawSocket {
sockfd: sockfd,
buffer: buffer
})
}
let mut buffer = vec::Vec::new();
buffer.resize(try!(lower.interface_mtu()), 0);
Ok(RawSocket {
lower: lower,
buffer: buffer
})
}
}
impl Drop for RawSocket {
fn drop(&mut self) {
unsafe { libc::close(self.sockfd); }
}
}
impl super::Device for RawSocket {
impl Device for RawSocket {
const MTU: usize = 1536;
fn recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
let len = unsafe {
let len = libc::recv(self.sockfd, self.buffer.as_mut_ptr() as *mut libc::c_void,
self.buffer.len(), 0);
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
len
};
handler(&self.buffer[..len as usize])
let len = self.lower.recv(&mut self.buffer[..]).unwrap();
handler(&self.buffer[..len])
}
fn send<F: FnOnce(&mut [u8])>(&mut self, size: usize, handler: F) {
handler(&mut self.buffer[..size]);
unsafe {
let len = libc::send(self.sockfd, self.buffer.as_ptr() as *const libc::c_void,
size, 0);
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
}
fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F) {
handler(&mut self.buffer[..len]);
self.lower.send(&self.buffer[..len]).unwrap();
}
}

11
src/phy/sys/linux.rs Normal file
View File

@ -0,0 +1,11 @@
use libc;
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
pub const IFF_TAP: libc::c_int = 0x0002;
pub const IFF_NO_PI: libc::c_int = 0x1000;
pub const ETH_P_ALL: libc::c_short = 0x0003;

42
src/phy/sys/mod.rs Normal file
View File

@ -0,0 +1,42 @@
use libc;
use std::io;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod imp;
pub mod raw_socket;
#[cfg(target_os = "linux")]
pub mod tap_interface;
pub use self::raw_socket::RawSocketDesc;
#[cfg(target_os = "linux")]
pub use self::tap_interface::TapInterfaceDesc;
#[repr(C)]
#[derive(Debug)]
struct ifreq {
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
}
fn ifreq_for(name: &str) -> ifreq {
let mut ifreq = ifreq {
ifr_name: [0; libc::IF_NAMESIZE],
ifr_data: 0
};
for (i, byte) in name.as_bytes().iter().enumerate() {
ifreq.ifr_name[i] = *byte as libc::c_char
}
ifreq
}
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
cmd: libc::c_ulong) -> io::Result<libc::c_int> {
unsafe {
let res = libc::ioctl(lower, cmd, ifreq as *mut ifreq);
if res == -1 { return Err(io::Error::last_os_error()) }
}
Ok(ifreq.ifr_data)
}

74
src/phy/sys/raw_socket.rs Normal file
View File

@ -0,0 +1,74 @@
use libc;
use std::{mem, io};
use super::*;
#[derive(Debug)]
pub struct RawSocketDesc {
lower: libc::c_int,
ifreq: ifreq
}
impl RawSocketDesc {
pub fn new(name: &str) -> io::Result<RawSocketDesc> {
let lower = unsafe {
let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW,
imp::ETH_P_ALL.to_be() as i32);
if lower == -1 { return Err(io::Error::last_os_error()) }
lower
};
Ok(RawSocketDesc {
lower: lower,
ifreq: ifreq_for(name)
})
}
pub fn interface_mtu(&mut self) -> io::Result<usize> {
ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)
}
pub fn bind_interface(&mut self) -> io::Result<()> {
let sockaddr = libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_protocol: imp::ETH_P_ALL.to_be() as u16,
sll_ifindex: try!(ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)),
sll_hatype: 1,
sll_pkttype: 0,
sll_halen: 6,
sll_addr: [0; 8]
};
unsafe {
let res = libc::bind(self.lower,
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
mem::size_of::<libc::sockaddr_ll>() as u32);
if res == -1 { return Err(io::Error::last_os_error()) }
}
Ok(())
}
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(), 0);
if len == -1 { return Err(io::Error::last_os_error()) }
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void,
buffer.len(), 0);
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
Ok(len as usize)
}
}
}
impl Drop for RawSocketDesc {
fn drop(&mut self) {
unsafe { libc::close(self.lower); }
}
}

View File

@ -0,0 +1,55 @@
use libc;
use std::io;
use super::*;
#[cfg(target_os = "linux")]
#[derive(Debug)]
pub struct TapInterfaceDesc {
lower: libc::c_int,
ifreq: ifreq
}
impl TapInterfaceDesc {
pub fn new(name: &str) -> io::Result<TapInterfaceDesc> {
let lower = unsafe {
let lower = libc::open("/dev/net/tun".as_ptr() as *const libc::c_char,
libc::O_RDWR);
if lower == -1 { return Err(io::Error::last_os_error()) }
lower
};
Ok(TapInterfaceDesc {
lower: lower,
ifreq: ifreq_for(name)
})
}
pub fn attach_interface(&mut self) -> io::Result<()> {
self.ifreq.ifr_data = imp::IFF_TAP | imp::IFF_NO_PI;
ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
}
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len());
if len == -1 { return Err(io::Error::last_os_error()) }
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void,
buffer.len());
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
Ok(len as usize)
}
}
}
impl Drop for TapInterfaceDesc {
fn drop(&mut self) {
unsafe { libc::close(self.lower); }
}
}

42
src/phy/tap_interface.rs Normal file
View File

@ -0,0 +1,42 @@
use std::{vec, io};
use super::{sys, Device};
/// A virtual Ethernet interface.
#[derive(Debug)]
pub struct TapInterface {
lower: sys::TapInterfaceDesc,
buffer: vec::Vec<u8>
}
impl TapInterface {
/// Attaches to a TAP interface called `name`, or creates it if it does not exist.
///
/// If `name` is a persistent interface configured with UID of the current user,
/// no special privileges are needed. Otherwise, this requires superuser privileges
/// or a corresponding capability set on the executable.
pub fn new(name: &str) -> io::Result<TapInterface> {
let mut lower = try!(sys::TapInterfaceDesc::new(name));
try!(lower.attach_interface());
let mut buffer = vec::Vec::new();
buffer.resize(Self::MTU, 0);
Ok(TapInterface {
lower: lower,
buffer: buffer
})
}
}
impl Device for TapInterface {
const MTU: usize = 1536;
fn recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
let len = self.lower.recv(&mut self.buffer[..]).unwrap();
handler(&self.buffer[..len])
}
fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F) {
handler(&mut self.buffer[..len]);
self.lower.send(&self.buffer[..len]).unwrap();
}
}