Skip to content

Commit

Permalink
Added libpcap-based backend
Browse files Browse the repository at this point in the history
  • Loading branch information
moosingin3space committed Mar 6, 2017
1 parent 8b64967 commit cf5255b
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Expand Up @@ -54,6 +54,10 @@ optional = true
version = "0.9"
optional = true

[dependencies.pcap]
version = "0.5.*"
optional = true

[dependencies.pnet_macros]
optional = true
default-features = false
Expand Down
4 changes: 4 additions & 0 deletions src/datalink/mod.rs
Expand Up @@ -54,8 +54,12 @@ mod backend;
#[cfg(feature = "netmap")]
pub mod netmap;

#[cfg(feature = "pcap")]
pub mod pcap;

pub mod dummy;


/// Type of data link channel to present (Linux only)
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ChannelType {
Expand Down
194 changes: 194 additions & 0 deletions src/datalink/pcap.rs
@@ -0,0 +1,194 @@
//! Support for sending and receiving data link layer packets using libpcap.
//! Also has support for reading pcap files.
extern crate pcap;

use std::marker::{Send, Sync};
use std::io;
use std::iter::repeat;
use self::pcap::{Active, Activated};
use datalink::{self, NetworkInterface};
use datalink::{EthernetDataLinkChannelIterator, EthernetDataLinkReceiver, EthernetDataLinkSender};
use datalink::Channel::Ethernet;
use packet::Packet;
use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::path::Path;

/// Configuration for the pcap datalink backend
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
/// The size of buffer to use when reading packets. Defaults to 4096
pub read_buffer_size: usize,

/// The read timeout. Defaults to None.
pub read_timeout: Option<Duration>,
}

impl<'a> From<&'a datalink::Config> for Config {
fn from(config: &datalink::Config) -> Config {
Config{
read_buffer_size: config.read_buffer_size,
read_timeout: config.read_timeout,
}
}
}

impl Default for Config {
fn default() -> Config {
Config{
read_buffer_size: 4096,
read_timeout: None,
}
}
}

/// Create a datalink channel from the provided pcap device
#[inline]
pub fn channel(network_interface: &NetworkInterface,
config: Config) -> io::Result<datalink::Channel> {
let cap = match pcap::Capture::from_device(&*network_interface.name) {
Ok(cap) => cap,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
}.buffer_size(config.read_buffer_size as i32);
let cap = match config.read_timeout {
Some(to) => cap.timeout(
(to.as_secs() * 1000 + (to.subsec_nanos() / 1000) as u64) as i32
),
None => cap
};
let cap = match cap.open() {
Ok(cap) => cap,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
};
let cap = Arc::new(Mutex::new(cap));
Ok(Ethernet(
Box::new(DataLinkSenderImpl {
capture: cap.clone(),
}),
Box::new(DataLinkReceiverImpl {
capture: cap.clone(),
read_buffer: repeat(0u8).take(config.read_buffer_size).collect(),
})
))
}

/// Create a datalink channel from a pcap file
#[inline]
pub fn from_file<P: AsRef<Path>>(path: P, config: Config) -> io::Result<datalink::Channel> {
let cap = match pcap::Capture::from_file(path) {
Ok(cap) => cap,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
};
let cap = Arc::new(Mutex::new(cap));
Ok(Ethernet(
Box::new(InvalidDataLinkSenderImpl {}),
Box::new(DataLinkReceiverImpl {
capture: cap.clone(),
read_buffer: repeat(0u8).take(config.read_buffer_size).collect(),
})
))
}

struct DataLinkSenderImpl {
capture: Arc<Mutex<pcap::Capture<Active>>>,
}

impl EthernetDataLinkSender for DataLinkSenderImpl {
#[inline]
fn build_and_send(&mut self,
num_packets: usize,
packet_size: usize,
func: &mut FnMut(MutableEthernetPacket))
-> Option<io::Result<()>> {
for _ in 0..num_packets {
let mut data = vec![0; packet_size];
{
let eh = MutableEthernetPacket::new(&mut data).unwrap();
func(eh);
}
let mut cap = self.capture.lock().unwrap();
if let Err(e) = cap.sendpacket(&data) {
return Some(Err(io::Error::new(io::ErrorKind::Other, e)))
}
}
Some(Ok(()))
}

#[inline]
fn send_to(&mut self,
packet: &EthernetPacket,
_dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
let mut cap = self.capture.lock().unwrap();
Some(match cap.sendpacket(packet.packet()) {
Ok(()) => Ok(()),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
})
}
}

struct InvalidDataLinkSenderImpl {}

impl EthernetDataLinkSender for InvalidDataLinkSenderImpl {
#[inline]
fn build_and_send(&mut self,
_num_packets: usize,
_packet_size: usize,
_func: &mut FnMut(MutableEthernetPacket))
-> Option<io::Result<()>> {
None
}

#[inline]
fn send_to(&mut self,
_packet: &EthernetPacket,
_dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
None
}
}

struct DataLinkReceiverImpl<T: Activated + Send + Sync> {
capture: Arc<Mutex<pcap::Capture<T>>>,
read_buffer: Vec<u8>,
}

impl <T: Activated + Send + Sync> EthernetDataLinkReceiver for DataLinkReceiverImpl<T> {
fn iter<'a>(&'a mut self) -> Box<EthernetDataLinkChannelIterator + 'a> {
Box::new(DataLinkChannelIteratorImpl { pc: self })
}
}

struct DataLinkChannelIteratorImpl<'a, T: Activated + Send + Sync + 'a> {
pc: &'a mut DataLinkReceiverImpl<T>,
}

impl<'a, T: Activated + Send + Sync> EthernetDataLinkChannelIterator<'a> for DataLinkChannelIteratorImpl<'a, T> {
fn next(&mut self) -> io::Result<EthernetPacket> {
let mut cap = self.pc.capture.lock().unwrap();
match cap.next() {
Ok(pkt) => {
self.pc.read_buffer.truncate(0);
self.pc.read_buffer.extend(pkt.data);
},
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
};
Ok(EthernetPacket::new(&self.pc.read_buffer).unwrap())
}
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
if let Ok(devices) = pcap::Device::list() {
devices.iter().enumerate().map(|(i, dev)| {
NetworkInterface {
name: dev.name.clone(),
index: i as u32,
mac: None,
ips: None,
flags: 0,
}
}).collect()
} else {
vec![]
}
}

0 comments on commit cf5255b

Please sign in to comment.