blob: c83fd238a005ca42bac11894985e5bf5edff6139 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Execution contexts.
//!
//! This module defines "context" traits, which allow code in this crate to be
//! written agnostic to their execution context.
//!
//! All of the code in this crate operates in terms of "events". When an event
//! occurs (for example, a packet is received, an application makes a request,
//! or a timer fires), a function is called to handle that event. In response to
//! that event, the code may wish to emit new events (for example, to send a
//! packet, to respond to an application request, or to install a new timer).
//! The traits in this module provide the ability to emit new events. For
//! example, if, in order to handle some event, we need the ability to install
//! new timers, then the function to handle that event would take a
//! [`TimerContext`] parameter, which it could use to install new timers.
//!
//! Structuring code this way allows us to write code which is agnostic to
//! execution context - a test fake or any number of possible "real-world"
//! implementations of these traits all appear as indistinguishable, opaque
//! trait implementations to our code.
//!
//! The benefits are deeper than this, though. Large units of code can be
//! subdivided into smaller units that view each other as "contexts". For
//! example, the ARP implementation in the [`crate::device::arp`] module defines
//! the [`ArpContext`] trait, which is an execution context for ARP operations.
//! It is implemented both by the test fakes in that module, and also by the
//! Ethernet device implementation in the [`crate::device::ethernet`] module.
//!
//! This subdivision of code into small units in turn enables modularity. If,
//! for example, the IP code sees transport layer protocols as execution
//! contexts, then customizing which transport layer protocols are supported is
//! just a matter of providing a different implementation of the transport layer
//! context traits (this isn't what we do today, but we may in the future).
use lock_order::Unlocked;
use crate::{
marker::{BindingsContext, BindingsTypes},
state::StackState,
};
pub use netstack3_base::{
ContextPair, ContextProvider, CoreEventContext, CoreTimerContext, CounterContext, CtxPair,
DeferredResourceRemovalContext, EventContext, HandleableTimer, InstantBindingsTypes,
InstantContext, NestedIntoCoreTimerCtx, ReceivableFrameMeta, RecvFrameContext,
ReferenceNotifiers, ResourceCounterContext, RngContext, SendFrameContext, SendableFrameMeta,
TimerBindingsTypes, TimerContext, TimerHandler, TracingContext,
};
// Enable all blanket implementations on CoreCtx.
//
// Some blanket implementations are enabled individually to sidestep coherence
// issues with the fake context implementations in tests. We treat each of them
// individually so it's easier to split things into separate crates and avoids
// playing whack-a-mole with single markers that work for some traits/crates but
// not others.
impl<BC: BindingsContext, L> crate::ip::base::UseTransportIpContextBlanket for CoreCtx<'_, BC, L> {}
impl<BC: BindingsContext, L> crate::ip::base::UseIpSocketContextBlanket for CoreCtx<'_, BC, L> {}
impl<BC: BindingsContext, L> crate::ip::socket::UseIpSocketHandlerBlanket for CoreCtx<'_, BC, L> {}
impl<BC: BindingsContext, L> crate::ip::socket::UseDeviceIpSocketHandlerBlanket
for CoreCtx<'_, BC, L>
{
}
impl<BC: BindingsContext, L> crate::transport::udp::UseUdpIpTransportContextBlanket
for CoreCtx<'_, BC, L>
{
}
impl<BC: BindingsContext, L> crate::device::ethernet::UseArpFrameMetadataBlanket
for CoreCtx<'_, BC, L>
{
}
/// Provides access to core context implementations.
///
/// `L` is the current lock level of `CoreCtx`. The alias [`UnlockedCoreCtx`] is
/// provided at the [`Unlocked`] level.
pub type CoreCtx<'a, BT, L> = Locked<&'a StackState<BT>, L>;
pub(crate) type CoreCtxAndResource<'a, BT, R, L> =
Locked<lock_order::OwnedTupleWrapper<&'a StackState<BT>, &'a R>, L>;
/// An alias for an unlocked [`CoreCtx`].
pub type UnlockedCoreCtx<'a, BT> = CoreCtx<'a, BT, Unlocked>;
pub(crate) use locked::Locked;
impl<'a, BT, L> ContextProvider for CoreCtx<'a, BT, L>
where
BT: BindingsTypes,
{
type Context = Self;
fn context(&mut self) -> &mut Self::Context {
self
}
}
/// Provides a crate-local wrapper for `[lock_order::Locked]`.
///
/// This module is intentionally private so usage is limited to the type alias
/// in [`CoreCtx`].
mod locked {
use super::{BindingsTypes, CoreCtx, StackState};
use core::ops::Deref;
use lock_order::{wrap::LockedWrapper, Locked as ExternalLocked, TupleWrapper, Unlocked};
/// A crate-local wrapper on [`lock_order::Locked`].
pub struct Locked<T, L>(ExternalLocked<T, L>);
impl<T, L> LockedWrapper<T, L> for Locked<T, L>
where
T: Deref,
T::Target: Sized,
{
type AtLockLevel<'l, M> = Locked<&'l T::Target, M>
where
M: 'l,
T: 'l;
type CastWrapper<X> = Locked<X, L>
where
X: Deref,
X::Target: Sized;
fn wrap<'l, M>(locked: ExternalLocked<&'l T::Target, M>) -> Self::AtLockLevel<'l, M>
where
M: 'l,
T: 'l,
{
Locked(locked)
}
fn wrap_cast<R: Deref>(locked: ExternalLocked<R, L>) -> Self::CastWrapper<R>
where
R::Target: Sized,
{
Locked(locked)
}
fn get_mut(&mut self) -> &mut ExternalLocked<T, L> {
let Self(locked) = self;
locked
}
fn get(&self) -> &ExternalLocked<T, L> {
let Self(locked) = self;
locked
}
}
impl<'a, BT: BindingsTypes> CoreCtx<'a, BT, Unlocked> {
/// Creates a new `CoreCtx` from a borrowed [`StackState`].
pub fn new(stack_state: &'a StackState<BT>) -> Self {
Self(ExternalLocked::new(stack_state))
}
}
impl<'a, BT, R, L, T> Locked<T, L>
where
R: 'a,
T: Deref<Target = TupleWrapper<&'a StackState<BT>, &'a R>>,
BT: BindingsTypes,
{
pub(crate) fn cast_resource(&mut self) -> Locked<&'_ R, L> {
let Self(locked) = self;
Locked(locked.cast_with(|c| c.right()))
}
pub(crate) fn cast_core_ctx(&mut self) -> CoreCtx<'_, BT, L> {
let Self(locked) = self;
crate::CoreCtx::<BT, L>::wrap(locked.cast_with(|c| c.left()))
}
}
}
/// Fake implementations of context traits.
#[cfg(any(test, feature = "testutils"))]
pub(crate) mod testutil {
use alloc::sync::Arc;
use core::fmt::Debug;
#[cfg(test)]
use crate::filter::{FilterBindingsTypes, FilterHandlerProvider};
use crate::{
device::link::LinkDevice,
ip::device::nud::{LinkResolutionContext, LinkResolutionNotifier},
sync::Mutex,
};
pub use netstack3_base::testutil::{
FakeBindingsCtx, FakeCoreCtx, FakeCryptoRng, FakeEventCtx, FakeFrameCtx, FakeInstant,
FakeInstantCtx, FakeNetwork, FakeNetworkContext, FakeNetworkLinks, FakeTimerCtx,
FakeTimerCtxExt, FakeTracingCtx, InstantAndData, PendingFrame, PendingFrameData,
StepResult, WithFakeFrameContext, WithFakeTimerContext,
};
impl<D: LinkDevice, Id, Event: Debug, State, FrameMeta> LinkResolutionContext<D>
for FakeBindingsCtx<Id, Event, State, FrameMeta>
{
type Notifier = FakeLinkResolutionNotifier<D>;
}
/// A fake implementation of [`LinkResolutionNotifier`].
#[derive(Debug)]
pub struct FakeLinkResolutionNotifier<D: LinkDevice>(
Arc<Mutex<Option<Result<D::Address, crate::error::AddressResolutionFailed>>>>,
);
impl<D: LinkDevice> LinkResolutionNotifier<D> for FakeLinkResolutionNotifier<D> {
type Observer =
Arc<Mutex<Option<Result<D::Address, crate::error::AddressResolutionFailed>>>>;
fn new() -> (Self, Self::Observer) {
let inner = Arc::new(Mutex::new(None));
(Self(inner.clone()), inner)
}
fn notify(self, result: Result<D::Address, crate::error::AddressResolutionFailed>) {
let Self(inner) = self;
let mut inner = inner.lock();
assert_eq!(*inner, None, "resolved link address was set more than once");
*inner = Some(result);
}
}
#[cfg(test)]
impl<CC, TimerId, Event: Debug, State> WithFakeTimerContext<TimerId>
for FakeCtxWithCoreCtx<CC, TimerId, Event, State>
{
fn with_fake_timer_ctx<O, F: FnOnce(&FakeTimerCtx<TimerId>) -> O>(&self, f: F) -> O {
let Self { core_ctx: _, bindings_ctx } = self;
f(&bindings_ctx.timers)
}
fn with_fake_timer_ctx_mut<O, F: FnOnce(&mut FakeTimerCtx<TimerId>) -> O>(
&mut self,
f: F,
) -> O {
let Self { core_ctx: _, bindings_ctx } = self;
f(&mut bindings_ctx.timers)
}
}
#[cfg(test)]
pub(crate) type FakeCtxWithCoreCtx<CC, TimerId, Event, BindingsCtxState> =
crate::testutil::ContextPair<CC, FakeBindingsCtx<TimerId, Event, BindingsCtxState, ()>>;
#[cfg(test)]
pub(crate) type FakeCtx<S, TimerId, Meta, Event, DeviceId, BindingsCtxState> =
FakeCtxWithCoreCtx<FakeCoreCtx<S, Meta, DeviceId>, TimerId, Event, BindingsCtxState>;
#[cfg(test)]
impl<CC, Id, Event: Debug, BindingsCtxState> AsRef<FakeInstantCtx>
for FakeCtxWithCoreCtx<CC, Id, Event, BindingsCtxState>
{
fn as_ref(&self) -> &FakeInstantCtx {
&self.bindings_ctx.timers.instant
}
}
#[cfg(test)]
impl<CC, Id, Event: Debug, BindingsCtxState> AsRef<FakeTimerCtx<Id>>
for FakeCtxWithCoreCtx<CC, Id, Event, BindingsCtxState>
{
fn as_ref(&self) -> &FakeTimerCtx<Id> {
&self.bindings_ctx.timers
}
}
#[cfg(test)]
impl<CC, Id, Event: Debug, BindingsCtxState> AsMut<FakeTimerCtx<Id>>
for FakeCtxWithCoreCtx<CC, Id, Event, BindingsCtxState>
{
fn as_mut(&mut self) -> &mut FakeTimerCtx<Id> {
&mut self.bindings_ctx.timers
}
}
#[cfg(test)]
impl<S, Id, Meta, Event: Debug, DeviceId, BindingsCtxState> AsMut<FakeFrameCtx<Meta>>
for FakeCtx<S, Id, Meta, Event, DeviceId, BindingsCtxState>
{
fn as_mut(&mut self) -> &mut FakeFrameCtx<Meta> {
&mut self.core_ctx.frames
}
}
#[cfg(test)]
impl<S, Id, Meta, Event: Debug, DeviceId, BindingsCtxState> WithFakeFrameContext<Meta>
for FakeCtx<S, Id, Meta, Event, DeviceId, BindingsCtxState>
{
fn with_fake_frame_ctx_mut<O, F: FnOnce(&mut FakeFrameCtx<Meta>) -> O>(
&mut self,
f: F,
) -> O {
f(&mut self.core_ctx.frames)
}
}
#[cfg(test)]
impl<I: packet_formats::ip::IpExt, BC: FilterBindingsTypes, S, Meta, DeviceId>
FilterHandlerProvider<I, BC> for FakeCoreCtx<S, Meta, DeviceId>
{
type Handler<'a> = crate::filter::NoopImpl where Self: 'a;
fn filter_handler(&mut self) -> Self::Handler<'_> {
crate::filter::NoopImpl
}
}
}