Skip to content

Instantly share code, notes, and snippets.

@polachok
Created May 28, 2016 23:56
Show Gist options
  • Save polachok/5238b877e6d5b5f367c74e89576e5a57 to your computer and use it in GitHub Desktop.
Save polachok/5238b877e6d5b5f367c74e89576e5a57 to your computer and use it in GitHub Desktop.
Netmap wrapper API
extern crate netmap_sys;
use ::libc;
use std::mem;
use std::ptr;
use std::slice;
use std::iter::Iterator;
use std::ffi::{CStr,CString};
use self::netmap_sys::netmap;
use self::netmap_sys::netmap_user;
/// Forward slot
pub use self::netmap_sys::netmap::NS_FORWARD;
/// Indicate that buffer was changed
pub use self::netmap_sys::netmap::NS_BUF_CHANGED;
/// Report when sent
pub use self::netmap_sys::netmap::NS_REPORT;
/// Enable forwarding on ring
pub use self::netmap_sys::netmap::NR_FORWARD;
#[derive(Debug)]
#[allow(dead_code)]
pub enum Direction {
Input,
Output,
InputOutput
}
#[derive(Debug)]
pub struct NetmapError {
msg: String,
}
impl NetmapError {
fn new(msg: String) -> Self {
NetmapError { msg: msg }
}
}
pub trait NetmapSlot {
fn get_len(&self) -> u16;
fn get_flags(&self) -> u16;
fn set_flags(&mut self, flag: u16);
fn get_buf_idx(&self) -> u32;
fn set_buf_idx(&mut self, idx: u32);
}
pub struct RxSlot(netmap::netmap_slot);
impl NetmapSlot for RxSlot {
fn get_len(&self) -> u16 {
self.0.len
}
fn set_flags(&mut self, flag: u16) {
self.0.flags |= flag;
}
fn get_flags(&self) -> u16 {
self.0.flags
}
fn get_buf_idx(&self) -> u32 {
self.0.buf_idx
}
fn set_buf_idx(&mut self, idx: u32) {
self.0.buf_idx = idx
}
}
impl RxSlot {
#[inline]
pub fn get_buf<'b,'a>(&'a self, ring: &RxRing) -> &'b [u8] {
let buf_idx = self.0.buf_idx;
let buf = unsafe { netmap_user::NETMAP_BUF(mem::transmute(ring), buf_idx as isize) as *const u8 };
unsafe { slice::from_raw_parts::<u8>(buf, self.0.len as usize) }
}
}
pub struct TxSlot(netmap::netmap_slot);
impl NetmapSlot for TxSlot {
#[inline]
fn get_len(&self) -> u16 {
self.0.len
}
#[inline]
fn set_flags(&mut self, flag: u16) {
self.0.flags |= flag;
}
#[inline]
fn get_flags(&self) -> u16 {
self.0.flags
}
#[inline]
fn get_buf_idx(&self) -> u32 {
self.0.buf_idx
}
#[inline]
fn set_buf_idx(&mut self, idx: u32) {
self.0.buf_idx = idx
}
}
impl TxSlot {
#[inline]
pub fn get_buf_mut<'b,'a>(&'a mut self, ring: &TxRing) -> &'b mut [u8] {
let buf_idx = self.0.buf_idx;
let buf = unsafe { netmap_user::NETMAP_BUF(mem::transmute(ring), buf_idx as isize) as *mut u8 };
unsafe { slice::from_raw_parts_mut::<u8>(buf, self.0.len as usize) }
}
#[inline]
pub fn set_len(&mut self, len: u16) {
self.0.len = len;
}
}
pub trait NetmapRing {
fn id(&self) -> u16;
fn is_empty(&self) -> bool;
fn next_slot(&mut self);
fn set_flags(&mut self, flag: u32);
}
pub struct RxRing(netmap::netmap_ring);
impl RxRing {
#[allow(dead_code)]
#[inline]
pub fn get_slot_mut<'a,'b>(&'a self) -> &'b mut RxSlot {
let cur = self.0.cur;
let slots = &self.0.slot as *const netmap::netmap_slot;
unsafe { mem::transmute(slots.offset(cur as isize)) }
}
#[inline]
pub fn iter(&mut self) -> RxSlotIter {
let cur = self.0.cur;
RxSlotIter {
ring: self,
cur: cur,
}
}
}
impl NetmapRing for RxRing {
#[inline]
fn id(&self) -> u16 {
self.0.ringid
}
#[inline]
fn is_empty(&self) -> bool {
self.0.cur == self.0.tail
}
#[inline]
fn set_flags(&mut self, flag: u32) {
self.0.flags |= flag;
}
#[inline]
fn next_slot(&mut self) {
self.0.cur = if self.0.cur + 1 == self.0.num_slots { 0 } else { self.0.cur + 1 };
self.0.head = self.0.cur;
}
}
pub struct RxSlotIter<'a> {
ring: &'a mut RxRing,
cur: u32,
}
impl<'a> Iterator for RxSlotIter<'a> {
type Item = (&'a mut RxSlot, &'a [u8]);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.ring.0.cur = self.cur;
self.ring.0.head = self.ring.0.cur;
if self.ring.is_empty() {
return None;
}
let cur = self.cur;
let slots = self.ring.0.slot.as_mut_ptr();
let slot: &mut RxSlot = unsafe { mem::transmute(slots.offset(cur as isize)) };
let buf = slot.get_buf(self.ring);
self.cur = if self.cur + 1 == self.ring.0.num_slots { 0 } else { self.cur + 1 };
Some((slot, buf))
}
}
impl<'a> Drop for RxSlotIter<'a> {
fn drop(&mut self) {
self.ring.0.cur = self.cur;
self.ring.0.head = self.ring.0.cur;
}
}
pub struct RxRingIter<'d> {
cur: u16,
last: u16,
netmap: &'d NetmapDescriptor,
}
impl<'d> Iterator for RxRingIter<'d> {
type Item = &'d mut RxRing;
#[inline]
fn next<'a>(&'a mut self) -> Option<&'d mut RxRing> {
if self.cur > self.last {
return None;
}
let rx_ring = {
let cur = self.cur.clone();
self.netmap.get_rx_ring(cur)
};
self.cur += 1;
Some(rx_ring)
}
}
pub struct TxRing(netmap::netmap_ring);
impl TxRing {
#[inline]
pub fn get_slot_mut<'a,'b>(&'a self) -> &'b mut TxSlot {
let cur = self.0.cur;
let slots = &self.0.slot as *const netmap::netmap_slot;
unsafe { mem::transmute(slots.offset(cur as isize)) }
}
#[inline]
pub fn iter(&mut self) -> TxSlotIter {
let cur = self.0.cur;
TxSlotIter {
ring: self,
cur: cur,
}
}
}
impl NetmapRing for TxRing {
#[inline]
fn id(&self) -> u16 {
self.0.ringid
}
#[inline]
fn is_empty(&self) -> bool {
self.0.cur == self.0.tail
}
#[inline]
fn set_flags(&mut self, flag: u32) {
self.0.flags |= flag;
}
#[inline]
fn next_slot(&mut self) {
self.0.cur = if self.0.cur + 1 == self.0.num_slots { 0 } else { self.0.cur + 1 };
self.0.head = self.0.cur;
}
}
impl<'a> Iterator for &'a mut TxRing {
type Item = &'a mut TxSlot;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let cur = self.0.cur;
let slots = &self.0.slot as *const netmap::netmap_slot;
let slot = unsafe { mem::transmute(slots.offset(cur as isize)) };
self.next_slot();
slot
}
}
pub struct TxRingIter<'d> {
last: u16,
cur: u16,
netmap: &'d NetmapDescriptor,
}
impl<'d> Iterator for TxRingIter<'d> {
type Item = &'d mut TxRing;
#[inline]
fn next<'a>(&'a mut self) -> Option<&'d mut TxRing> {
if self.cur > self.last {
return None;
}
let tx_ring = {
let cur = self.cur.clone();
self.netmap.get_tx_ring(cur)
};
self.cur += 1;
Some(tx_ring)
}
}
/// Slot and buffer iterator
pub struct TxSlotIter<'a> {
ring: &'a mut TxRing,
cur: u32
}
impl<'a> Iterator for TxSlotIter<'a> {
type Item = (&'a mut TxSlot, &'a mut [u8]);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.ring.0.cur = self.cur;
self.ring.0.head = self.ring.0.cur;
if self.ring.is_empty() {
return None;
}
let cur = self.cur;
let slots = self.ring.0.slot.as_mut_ptr();
let slot: &mut TxSlot = unsafe { mem::transmute(slots.offset(cur as isize)) };
slot.set_len(2048);
let buf = slot.get_buf_mut(self.ring);
self.cur = if self.cur + 1 == self.ring.0.num_slots { 0 } else { self.cur + 1 };
Some((slot, buf))
}
}
impl<'a> Drop for TxSlotIter<'a> {
fn drop(&mut self) {
self.ring.0.cur = self.cur;
self.ring.0.head = self.ring.0.cur;
}
}
/// Netmap descriptor wrapper
pub struct NetmapDescriptor {
raw: *mut netmap_user::nm_desc
}
unsafe impl Send for NetmapDescriptor {}
impl NetmapDescriptor {
pub fn new(iface: &str) -> Result<Self, NetmapError> {
let base_nmd: netmap::nmreq = unsafe { mem::zeroed() };
let netmap_iface = CString::new(format!("netmap:{}", iface)).unwrap();
let netmap_desc = unsafe { netmap_user::nm_open(netmap_iface.as_ptr(), &base_nmd, 0, ptr::null()) };
if netmap_desc == ptr::null_mut() {
return Err(NetmapError::new(format!("Can't open {:?}", netmap_iface)));
}
Ok(NetmapDescriptor {
raw: netmap_desc
})
}
pub fn new_with_memory(iface: &str, parent: &NetmapDescriptor) -> Result<Self, NetmapError> {
let base_nmd: netmap::nmreq = unsafe { mem::zeroed() };
let netmap_iface = CString::new(format!("netmap:{}", iface)).unwrap();
let netmap_desc = unsafe { netmap_user::nm_open(netmap_iface.as_ptr(), &base_nmd, netmap_user::NM_OPEN_NO_MMAP as u64, parent.raw) };
if netmap_desc == ptr::null_mut() {
return Err(NetmapError::new(format!("Can't open {:?}", netmap_iface)));
}
Ok(NetmapDescriptor {
raw: netmap_desc
})
}
pub fn rx_iter<'i, 'd: 'i>(&'d mut self) -> RxRingIter<'i> {
let (first, last) = self.get_rx_rings();
RxRingIter {
last: last,
cur: first,
netmap: self,
}
}
pub fn tx_iter<'i, 'd: 'i>(&'d mut self) -> TxRingIter<'i> {
let (first, last) = self.get_tx_rings();
TxRingIter {
last: last,
cur: first,
netmap: self,
}
}
pub fn clone_ring(&self, ring: u16, dir: Direction) -> Result<Self,NetmapError> {
let mut nm_desc_raw: netmap_user::nm_desc = unsafe { (*(self.raw)) };
/* XXX: check that we opened it with ALL_NIC before */
let (flag, ring_flag) = match dir {
Direction::Input => (netmap::NR_RX_RINGS_ONLY, netmap::NETMAP_NO_TX_POLL),
Direction::Output => (netmap::NR_TX_RINGS_ONLY, 0),
Direction::InputOutput => (0, 0),
};
nm_desc_raw.req.nr_flags = netmap::NR_REG_ONE_NIC as u32 | flag as u32;
if ring == self.get_rx_rings_count() { nm_desc_raw.req.nr_flags = netmap::NR_REG_SW as u32 | flag };
nm_desc_raw.req.nr_ringid = ring | ring_flag as u16;
nm_desc_raw.self_ = &mut nm_desc_raw;
let ifname = unsafe { CStr::from_ptr(nm_desc_raw.req.nr_name.as_ptr()).to_str().unwrap() };
let netmap_ifname = CString::new(format!("netmap:{}", ifname)).unwrap();
let netmap_desc = unsafe {
netmap_user::nm_open(netmap_ifname.as_ptr(),
ptr::null(),
netmap_user::NM_OPEN_NO_MMAP as u64 | netmap_user::NM_OPEN_IFNAME as u64 /* | flag as u64 */,
&mut nm_desc_raw)
};
if netmap_desc == ptr::null_mut() {
return Err(NetmapError::new(format!("Can't open ring {}", ring)));
}
Ok(NetmapDescriptor {
raw: netmap_desc
})
}
pub fn get_rx_rings_count(&self) -> u16 {
let nifp = unsafe { (*self.raw).nifp };
unsafe { (*nifp).ni_rx_rings as u16 }
}
pub fn get_tx_rings_count(&self) -> u16 {
let nifp = unsafe { (*self.raw).nifp };
unsafe { (*nifp).ni_tx_rings as u16 }
}
#[allow(dead_code)]
pub fn get_flags(&self) -> u32 {
unsafe { (*self.raw).req.nr_flags }
}
pub fn get_rx_rings(&self) -> (u16,u16) {
unsafe { ((*self.raw).first_rx_ring, (*self.raw).last_rx_ring) }
}
pub fn get_tx_rings(&self) -> (u16,u16) {
unsafe { ((*self.raw).first_tx_ring, (*self.raw).last_tx_ring) }
}
#[inline]
fn get_tx_ring(&self, ring: u16) -> &mut TxRing {
let nifp = unsafe { (*self.raw).nifp };
unsafe { mem::transmute(netmap_user::NETMAP_TXRING(nifp, ring as isize)) }
}
#[inline]
fn get_rx_ring(&self, ring: u16) -> &mut RxRing {
let nifp = unsafe { (*self.raw).nifp };
unsafe { mem::transmute(netmap_user::NETMAP_RXRING(nifp, ring as isize)) }
}
#[allow(dead_code)]
fn find_free_tx_ring(&self) -> Option<&mut TxRing> {
let (first, last) = self.get_tx_rings();
for ring in first..last+1 {
let tx_ring = self.get_tx_ring(ring);
if !tx_ring.is_empty() {
return Some(tx_ring);
}
}
return None;
}
#[allow(dead_code)]
pub fn get_fd(&self) -> i32 {
unsafe { (*self.raw).fd }
}
pub fn poll(&mut self, dir: Direction) -> Option<()> {
let fd = unsafe { (*self.raw).fd };
let mut pollfd: libc::pollfd = unsafe { mem::zeroed() };
pollfd.fd = fd;
pollfd.events = match dir {
Direction::Input => libc::POLLIN,
Direction::Output => libc::POLLOUT,
Direction::InputOutput => libc::POLLIN | libc::POLLOUT,
};
let rv = unsafe { libc::poll(&mut pollfd, 1, 1000) };
if rv <= 0 {
return None;
}
if pollfd.revents & libc::POLLERR == libc::POLLERR {
println!("POLLERR!");
return None;
}
Some(())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment