Skip to content

Escape from promiscuous mode with packet siphons #743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions lib/opte/src/ddi/mblk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,34 @@ pub trait AsMblk {
/// Consume `self`, returning the underlying `mblk_t`. The caller of this
/// function now owns the underlying segment chain.
fn unwrap_mblk(self) -> Option<NonNull<mblk_t>>;

/// Consume `self`, returning the underlying `mblk_t` and a pointer to the tail
/// element. The caller of this function now owns the underlying segment chain.
///
/// If the chain contains a single element, then the tail will be equal to the
/// head.
fn unwrap_head_and_tail(self) -> Option<(NonNull<mblk_t>, NonNull<mblk_t>)>
where
Self: Sized,
{
// SAFETY: `v`, if present, is a valid mblk_t.
self.unwrap_mblk().map(|v| unsafe { (v, find_mblk_tail(v)) })
}
}

/// Find the last element in an `mblk_t` chain.
///
/// # SAFETY
/// `head` must point to a valid mblk_t, which must not contain any loops
/// in its `b_next` chain.
unsafe fn find_mblk_tail(head: NonNull<mblk_t>) -> NonNull<mblk_t> {
let mut tail = head;
unsafe {
while let Some(next_ptr) = NonNull::new((*tail.as_ptr()).b_next) {
tail = next_ptr;
}
}
tail
}

/// The head and tail of an mblk_t list.
Expand Down Expand Up @@ -86,12 +114,7 @@ impl MsgBlkChain {
let head = NonNull::new(mp).ok_or(WrapError::NullPtr)?;

// Walk the chain to find the tail, and support faster append.
let mut tail = head;
unsafe {
while let Some(next_ptr) = NonNull::new((*tail.as_ptr()).b_next) {
tail = next_ptr;
}
}
let tail = unsafe { find_mblk_tail(head) };

Ok(Self(Some(MsgBlkChainInner { head, tail })))
}
Expand Down Expand Up @@ -164,6 +187,12 @@ impl AsMblk for MsgBlkChain {
fn unwrap_mblk(mut self) -> Option<NonNull<mblk_t>> {
self.0.take().map(|v| v.head)
}

fn unwrap_head_and_tail(
mut self,
) -> Option<(NonNull<mblk_t>, NonNull<mblk_t>)> {
self.0.take().map(|v| (v.head, v.tail))
}
}

impl Drop for MsgBlkChain {
Expand Down Expand Up @@ -830,6 +859,15 @@ impl AsMblk for MsgBlk {
_ = ManuallyDrop::new(self);
Some(ptr_out)
}

fn unwrap_head_and_tail(
self,
) -> Option<(NonNull<mblk_t>, NonNull<mblk_t>)> {
// MsgBlk represents a single `mblk_t` with NULL `b_next`.
// Thus, tail is just the head.
let out = self.unwrap_mblk();
Some((out, out))
}
}

/// An interior node of an [`MsgBlk`]'s chain, accessed via iterator.
Expand Down
43 changes: 43 additions & 0 deletions xde/src/mac/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,49 @@ impl<P> Drop for MacPromiscHandle<P> {
}
}

/// Safe wrapper around `mac_siphon_set`/`mac_siphon_clear`.
#[derive(Debug)]
pub struct MacSiphon<P: MacClient> {
/// The MAC client this siphon callback is attached to.
parent: *const P,
}

impl<P: MacClient> MacSiphon<P> {
/// Register a promiscuous callback to receive packets on the underlying MAC.
pub fn new(
parent: Arc<P>,
siphon_fn: mac_siphon_fn,
) -> Result<Self, c_int> {
let mch = parent.mac_client_handle()?;
let parent = Arc::into_raw(parent);
let arg = parent as *mut c_void;

// SAFETY: `MacSiphon` keeps a reference to this `P` until it is removed,
// and so we can safely access it from the callback via the `arg`
// pointer.
unsafe {
mac_siphon_set(mch, siphon_fn, arg);
}

Ok(Self { parent })
}
}

impl<P: MacClient> Drop for MacSiphon<P> {
fn drop(&mut self) {
// Safety: the parent MAC we've attached this siphon to is guaranteed
// to live long enough to access again, since we have a refcount hold
// on it.
unsafe {
let parent = Arc::from_raw(self.parent);
let mac_client = parent
.mac_client_handle()
.expect("FATAL: cannot remove mac siphon from client");
mac_siphon_clear(mac_client);
};
}
}

/// Safe wrapper around a `mac_unicast_handle_t`.
#[derive(Debug)]
pub struct MacUnicastHandle {
Expand Down
13 changes: 13 additions & 0 deletions xde/src/mac/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ pub type mac_rx_fn = unsafe extern "C" fn(
*mut mblk_t,
boolean_t,
);
pub type mac_siphon_fn = unsafe extern "C" fn(
*mut c_void,
*mut mblk_t,
*mut *mut mblk_t,
*mut c_uint,
*mut usize,
) -> *mut mblk_t;

unsafe extern "C" {
pub type mac_handle;
Expand Down Expand Up @@ -133,6 +140,12 @@ unsafe extern "C" {
arg: *mut c_void,
);
pub fn mac_rx_clear(mch: *const mac_client_handle);
pub fn mac_siphon_set(
mch: *const mac_client_handle,
rx_fn: mac_siphon_fn,
arg: *mut c_void,
);
pub fn mac_siphon_clear(mch: *const mac_client_handle);
pub fn mac_tx(
mch: *const mac_client_handle,
mp_chain: *mut mblk_t,
Expand Down
10 changes: 5 additions & 5 deletions xde/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::ip;
use crate::sys;
use crate::xde::DropRef;
use crate::xde::XdeDev;
use crate::xde::xde_underlay_port;
use crate::xde::XdeUnderlayPort;
use alloc::collections::BTreeMap;
use alloc::collections::btree_map::Entry;
use alloc::sync::Arc;
Expand Down Expand Up @@ -273,7 +273,7 @@ fn netstack_rele(ns: *mut ip::netstack_t) {
fn next_hop<'a>(
key: &RouteKey,
ustate: &'a XdeDev,
) -> Result<Route<'a>, &'a xde_underlay_port> {
) -> Result<Route<'a>, &'a XdeUnderlayPort> {
let RouteKey { dst: ip6_dst, l4_hash } = key;
unsafe {
// Use the GZ's routing table.
Expand Down Expand Up @@ -657,20 +657,20 @@ impl CachedRoute {
pub struct Route<'a> {
pub src: EtherAddr,
pub dst: EtherAddr,
pub underlay_dev: &'a xde_underlay_port,
pub underlay_dev: &'a XdeUnderlayPort,
}

impl<'a> Route<'a> {
fn cached(&self, xde: &XdeDev, timestamp: Moment) -> CachedRoute {
// As unfortunate as `into_route`.
let port_0: &xde_underlay_port = &xde.u1;
let port_0: &XdeUnderlayPort = &xde.u1;
let underlay_idx =
if core::ptr::eq(self.underlay_dev, port_0) { 0 } else { 1 };

CachedRoute { src: self.src, dst: self.dst, underlay_idx, timestamp }
}

fn zero_addr(underlay_dev: &'a xde_underlay_port) -> Route<'a> {
fn zero_addr(underlay_dev: &'a XdeUnderlayPort) -> Route<'a> {
Self { src: EtherAddr::zero(), dst: EtherAddr::zero(), underlay_dev }
}
}
Loading