diff --git a/Cargo.toml b/Cargo.toml
index 48a845b..7cf4ccf 100644
@@ -32,5 +32,14 @@ optional = true
[dependencies.pnet_macros]
path = "pnet_macros"
-[dev-dependencies]
+[dependencies]
time = "*"
+
+[[bin]]
+name = "rs_receiver"
+path = "benches/rs_receiver.rs"
+
+[[bin]]
+name = "rs_sender"
+path = "benches/rs_sender.rs"
+
diff --git a/benches/rs_sender.rs b/benches/rs_sender.rs
index 289863b..e6713e3 100644
@@ -10,6 +10,7 @@
#![feature(str_char)]
extern crate pnet;
+extern crate time;
use pnet::datalink::{datalink_channel};
use pnet::datalink::DataLinkChannelType::Layer2;
@@ -24,6 +25,7 @@ use pnet::util::get_network_interfaces;
use std::env;
use std::net::Ipv4Addr;
+use std::thread;
static IPV4_HEADER_LEN: usize = 20;
static UDP_HEADER_LEN: usize = 8;
@@ -79,8 +81,11 @@ pub fn build_udp4_packet(packet: &mut [u8], msg: &str) {
}
fn main() {
+ use std::iter::repeat;
+
let interface_name = env::args().nth(1).unwrap();
let destination = (&env::args().nth(2).unwrap()[..]).parse().unwrap();
+ let packet_size = (&env::args().nth(3).unwrap()[..]).parse().unwrap();
// Find the network interface with the provided name
let interfaces = get_network_interfaces();
let interface = interfaces.iter()
@@ -94,7 +99,8 @@ fn main() {
Err(e) => panic!("rs_sender: unable to create channel: {}", e)
};
- let mut buffer = [0u8; 64];
+ //let mut buffer = [0u8; 60];
+ let mut buffer: Vec<u8> = repeat(0u8).take(packet_size).collect();
let mut mut_ethernet_header = MutableEthernetPacket::new(&mut buffer[..]);
{
mut_ethernet_header.set_destination(destination);
@@ -105,8 +111,19 @@ fn main() {
let ethernet_header = EthernetPacket::new(mut_ethernet_header.packet());
- loop {
- tx.send_to(ðernet_header, None);
+ thread::sleep_ms(5000);
+
+// loop {
+ for i in 0..25 {
+ let start = time::precise_time_ns();
+ tx.build_and_send(10_000_000,
+ ethernet_header.packet().len(),
+ &mut |mut eh: MutableEthernetPacket| {
+ eh.clone_from(ðernet_header);
+ });
+ let end = time::precise_time_ns();
+ let duration = (end - start) as f64 / 1_000_000_000.0;
+ println!("{} pps (10_000_000 packets in {} seconds)", 10_000_000.0 / duration, duration);
}
}
diff --git a/src/bindings/mod.rs b/src/bindings/mod.rs
index 1da0b4d..958f9a3 100644
@@ -9,7 +9,7 @@
// NOTE Remove this once the next rust nightly is out.
pub mod libc;
-#[cfg(all(not(feature = "netmap"), any(target_os = "freebsd", target_os = "macos", windows)))]
+#[cfg(any(target_os = "freebsd", target_os = "macos", windows))]
pub mod bpf;
#[cfg(all(not(feature = "netmap"), target_os = "linux"))]
diff --git a/src/datalink/netmap.rs b/src/datalink/netmap.rs
index 57f324e..ff53aae 100644
@@ -12,49 +12,96 @@
extern crate netmap_sys;
extern crate libc;
-use libc::{c_int, c_uint, c_ulong, c_short};
use self::netmap_sys::netmap_user::{nm_open, nm_close, nm_nextpkt, nm_desc, nm_pkthdr,
nm_ring_next, NETMAP_TXRING, NETMAP_FD, NETMAP_BUF};
-use self::netmap_sys::netmap::{nm_ring_empty, netmap_slot};
+use self::netmap_sys::netmap::{nm_ring_empty, netmap_slot, NS_REPORT, netmap_if, netmap_ring};
+
+use self::libc::funcs::bsd44::sysctlbyname;
use std::ffi::CString;
-use std::path::Path;
-use std::fs::File;
use std::io;
-use std::io::Read;
use std::mem;
use std::num;
use std::ptr;
use std::raw;
use std::sync::Arc;
+use bindings::bpf;
use datalink::DataLinkChannelType;
use packet::Packet;
use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
use util::{NetworkInterface};
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[repr(C)]
struct pollfd {
- fd: c_int,
- events: c_short,
- revents: c_short
+ fd: libc::c_int,
+ events: libc::c_short,
+ revents: libc::c_short
}
+#[cfg(any(target_os = "linux", target_os = "freebsd"))]
+const POLLIN: libc::c_short = 0x0001;
+#[cfg(any(target_os = "linux", target_os = "freebsd"))]
+const POLLOUT: libc::c_short = 0x0004;
+
+#[cfg(target_os = "freebsd")]
+type nfds_t = libc::c_uint;
#[cfg(target_os = "linux")]
-const POLLIN: c_short = 0x0001;
-#[cfg(target_os = "linux")]
-const POLLOUT: c_short = 0x0004;
+type nfds_t = libc::c_ulong;
-type nfds_t = c_ulong;
+extern {
+ fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: libc::c_int) -> libc::c_int;
+}
+#[link(name = "nm_helper")]
extern {
- fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int;
+ fn netmap_txring(iface: *mut netmap_if, offset: isize) -> *mut netmap_ring;
+ fn netmap_buf(ring: *mut netmap_ring, offset: isize) -> *mut libc::c_void;
}
struct NmDesc {
desc: *mut nm_desc,
- buf_size: c_uint,
+ buf_size: libc::c_uint,
+}
+
+#[cfg(target_os = "linux")]
+fn build_desc(desc: *mut nm_desc) -> io::Result<NmDesc> {
+ use std::fs::File;
+ use std::path::Path;
+ use std::io::Read;
+
+ // FIXME the following try statements leak if they fail
+ let mut f = try!(File::open(&Path::new("/sys/module/netmap/parameters/buf_size")));
+ let mut num_str = String::new();
+ try!(f.read_to_string(&mut num_str));
+ let buf_size = num_str.trim_right().parse().unwrap();
+
+ Ok(NmDesc {
+ desc: desc,
+ buf_size: buf_size
+ })
+}
+
+#[cfg(target_os = "freebsd")]
+fn build_desc(desc: *mut nm_desc) -> io::Result<NmDesc> {
+ let name = CString::new(&b"dev.netmap.buf_size"[..]).unwrap().as_ptr();
+ let mut buf_size: libc::c_uint = 0;
+ let mut size_of_u32 = mem::size_of::<libc::c_uint>() as u64;
+ if unsafe {
+ sysctlbyname(name,
+ (&mut buf_size as *mut libc::c_uint) as *mut libc::c_void,
+ &mut size_of_u32,
+ ptr::null_mut(),
+ 0)
+ } == -1 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(NmDesc {
+ desc: desc,
+ buf_size: buf_size
+ })
+ }
}
impl NmDesc {
@@ -67,15 +114,7 @@ impl NmDesc {
if desc.is_null() {
Err(io::Error::last_os_error())
} else {
- let mut f = try!(File::open(&Path::new("/sys/module/netmap/parameters/buf_size")));
- let mut num_str = String::new();
- try!(f.read_to_string(&mut num_str));
- let buf_size = num_str.trim_right().parse().unwrap();
-
- Ok(NmDesc {
- desc: desc,
- buf_size: buf_size
- })
+ build_desc(desc)
}
}
}
@@ -98,7 +137,7 @@ impl DataLinkSenderImpl {
func: &mut F) -> Option<io::Result<()>>
where F : FnMut(MutableEthernetPacket)
{
- assert!(num::cast::<usize, u16>(packet_size).unwrap() as c_uint <= self.desc.buf_size);
+ assert!(num::cast::<usize, u16>(packet_size).unwrap() as libc::c_uint <= self.desc.buf_size);
let desc = self.desc.desc;
let mut fds = pollfd {
fd: unsafe { NETMAP_FD(desc) },
@@ -108,25 +147,42 @@ impl DataLinkSenderImpl {
let mut packet_idx = 0usize;
while packet_idx < num_packets {
unsafe {
- if poll(&mut fds, 1, -1) < 0 {
+ if poll(&mut fds, 1, 1000) <= 0 {
return Some(Err(io::Error::last_os_error()));
}
- let ring = NETMAP_TXRING((*desc).nifp, 0);
- while !nm_ring_empty(ring) && packet_idx < num_packets {
- let i = (*ring).cur;
+ for i in (*desc).first_tx_ring .. (*desc).last_tx_ring + 1 {
+ if packet_idx >= num_packets {
+ break;
+ }
+ //let ring = NETMAP_TXRING((*desc).nifp, i as isize);
+ let ring = netmap_txring((*desc).nifp, i as isize);
+ if nm_ring_empty(ring) {
+ continue;
+ }
+ //while !nm_ring_empty(ring) && packet_idx < num_packets {
+ // let i = (*ring).cur;
let slot_ptr: *mut netmap_slot = mem::transmute(&mut (*ring).slot);
- let buf = NETMAP_BUF(ring, (*slot_ptr.offset(i as isize)).buf_idx as isize);
+ let slot_ptr = slot_ptr.offset(i as isize);
+ let buf = netmap_buf(ring, (*slot_ptr).buf_idx as isize);
let slice = raw::Slice { data: buf, len: packet_size };
let meh = MutableEthernetPacket::new(mem::transmute(slice));
- (*slot_ptr.offset(i as isize)).len = packet_size as u16;
+ (*slot_ptr).len = packet_size as u16;
+ if packet_idx == num_packets - 1 {
+ (*slot_ptr).flags |= NS_REPORT as u16;
+ }
func(meh);
- let next = nm_ring_next(ring, i);
+ let next = nm_ring_next(ring, i as libc::c_uint);
(*ring).head = next;
(*ring).cur = next;
packet_idx += 1;
}
}
}
+ unsafe {
+ for i in 0..10 {
+ bpf::ioctl(fds.fd, 536897940/*NIOCTXSYNC*/, ptr::null::<libc::c_void>());
+ }
+ };
Some(Ok(()))
}
@@ -186,7 +242,7 @@ impl<'a> DataLinkChannelIteratorImpl<'a> {
events: POLLIN,
revents: 0,
};
- if unsafe { poll(&mut fds, 1, -1) } < 0 {
+ if unsafe { poll(&mut fds, 1, -1) } <= 0 {
return Err(io::Error::last_os_error());
}
buf = unsafe { nm_nextpkt(desc, &mut h) };