use std;
use std::io;
use std::mem;
use std::ptr;
use std::time::Duration;
use std::fs::{File, OpenOptions};
use std::os::unix::io::AsRawFd;
use std::collections::VecDeque;
use futures;
use libc;
use glob::glob;
use super::bindings::bpf;
struct Config {
buffer_size: usize,
read_timeout: Option<Duration>,
write_timeout: Option<Duration>,
}
impl Default for Config {
fn default() -> Config {
Config {
buffer_size: 4096,
read_timeout: None,
write_timeout: None,
}
}
}
fn pselect(nfds: &File,
readfds: Option<&mut libc::fd_set>,
writefds: Option<&mut libc::fd_set>,
timeout: Option<Duration>)
-> io::Result<()> {
let fd = nfds.as_raw_fd();
let timeout = timeout.map(|d| {
libc::timespec {
tv_sec: d.as_secs() as libc::time_t,
tv_nsec: d.subsec_nanos() as libc::c_long,
}
});
let ret = unsafe {
libc::pselect(fd + 1,
readfds
.map(|to| to as *mut libc::fd_set)
.unwrap_or(ptr::null_mut()),
writefds
.map(|to| to as *mut libc::fd_set)
.unwrap_or(ptr::null_mut()),
ptr::null_mut(),
timeout
.as_ref()
.map(|to| to as *const libc::timespec)
.unwrap_or(ptr::null()),
ptr::null())
};
match ret {
-1 => Err(io::Error::last_os_error()), 0 => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")),
_ => Ok(()),
}
}
pub struct Tap {
fd_set: libc::fd_set,
config: Config,
file: File,
}
impl Tap {
pub fn new(interface: &str) -> io::Result<Self> {
fn open() -> io::Result<File> {
for entry in glob("/dev/bpf*").expect("Failed to read glob pattern") {
if let Some(file) = entry
.ok()
.and_then(|path| {
OpenOptions::new()
.read(true)
.write(true)
.open(&path)
.ok()
}) {
return Ok(file);
}
}
Err(io::Error::last_os_error())
}
let file = try!(open());
let config = Config::default();
{
let fd = file.as_raw_fd();
let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
for (i, c) in interface.bytes().enumerate() {
iface.ifr_name[i] = c as i8;
}
let buflen = config.buffer_size as libc::c_uint;
ioctl!(fd, bpf::BIOCSBLEN, &buflen);
ioctl!(fd, bpf::BIOCSETIF, &iface);
let yes: libc::c_uint = 1;
ioctl!(fd, bpf::BIOCIMMEDIATE, &yes);
let mut dlt: libc::c_uint = 0;
ioctl!(fd, bpf::BIOCGDLT, &mut dlt);
match dlt {
bpf::DLT_NULL => {
ioctl!(fd, bpf::BIOCSSEESENT, &yes);
}
_ => {
ioctl!(fd, bpf::BIOCSHDRCMPLT, &yes);
}
}
{
use super::bindings::bpf::*;
let instructions = vec![bpf::BPF_STMT(bpf::BPF_RET + bpf::BPF_K, std::u32::MAX)];
ioctl!(fd,
BIOCSETF,
&bpf_program {
bf_len: instructions.len() as u32,
bf_insns: instructions.as_ptr(),
});
}
fcntl!(fd, libc::F_SETFL, libc::O_NONBLOCK);
}
let mut fd_set: libc::fd_set = unsafe { mem::zeroed() };
unsafe {
let fd = file.as_raw_fd();
let set = &mut fd_set as *mut libc::fd_set;
libc::FD_ZERO(set);
libc::FD_SET(fd, set);
}
Ok(Tap {
fd_set: fd_set,
config: config,
file: file,
})
}
pub fn stream(&mut self) -> Stream {
Stream {
buffer: vec![0u8; self.config.buffer_size],
packets: VecDeque::new(),
fd_set: &mut self.fd_set,
file: &self.file,
timeout: self.config.read_timeout,
}
}
pub fn sink(&mut self) -> Sink {
Sink {
fd_set: &mut self.fd_set,
file: &self.file,
timeout: self.config.write_timeout,
}
}
}
pub struct Stream<'a> {
buffer: Vec<u8>,
packets: VecDeque<(usize, usize)>,
fd_set: &'a mut libc::fd_set,
file: &'a File,
timeout: Option<Duration>,
}
impl<'a> futures::stream::Stream for Stream<'a> {
type Item = Vec<u8>;
type Error = io::Error;
fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
if self.packets.is_empty() {
let buffer = &mut self.buffer[..];
try!(pselect(self.file, Some(self.fd_set), None, self.timeout));
use std::io::Read;
let buflen = match self.file.read(buffer) {
Ok(len) if len > 0 => len,
_ => return Err(io::Error::last_os_error()),
};
let mut ptr = buffer.as_mut_ptr();
let end = unsafe { buffer.as_ptr().offset(buflen as isize) };
while (ptr as *const u8) < end {
unsafe {
let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
let start = ptr as isize + (*packet).bh_hdrlen as isize -
buffer.as_ptr() as isize;
self.packets
.push_back((start as usize, (*packet).bh_caplen as usize));
let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
}
}
}
let buffer = &self.buffer[..];
Ok(self.packets
.pop_front()
.map(move |(start, len)| Vec::from(&buffer[start..start + len]))
.into())
}
}
pub struct Sink<'a> {
fd_set: &'a mut libc::fd_set,
file: &'a File,
timeout: Option<Duration>,
}
impl<'a> Sink<'a> {
pub fn send(&mut self, packet: &[u8]) -> io::Result<()> {
use std::io::Write;
try!(pselect(self.file, None, Some(self.fd_set), self.timeout));
try!(self.file.write_all(packet));
Ok(())
}
}