| // Copyright 2018 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. |
| |
| //! A networking stack. |
| |
| // In case we roll the toolchain and something we're using as a feature has been |
| // stabilized. |
| #![allow(stable_features)] |
| #![feature(specialization)] |
| #![deny(missing_docs)] |
| #![deny(unreachable_patterns)] |
| // TODO(joshlf): Remove this once all of the elements in the crate are actually |
| // used. |
| #![allow(unused)] |
| #![deny(unused_imports)] |
| // This is a hack until we migrate to a different benchmarking framework. To run |
| // benchmarks, edit your Cargo.toml file to add a "benchmark" feature, and then |
| // run with that feature enabled. |
| #![cfg_attr(feature = "benchmark", feature(test))] |
| |
| #[cfg(all(test, feature = "benchmark"))] |
| extern crate test; |
| |
| #[macro_use] |
| mod macros; |
| |
| #[cfg(all(test, feature = "benchmark"))] |
| mod benchmarks; |
| mod device; |
| mod error; |
| mod ip; |
| #[cfg(test)] |
| mod testutil; |
| mod transport; |
| mod wire; |
| |
| pub use crate::device::{ |
| ethernet::Mac, get_ip_addr_subnet, receive_frame, DeviceId, DeviceLayerEventDispatcher, |
| }; |
| pub use crate::error::NetstackError; |
| pub use crate::ip::{ |
| AddrSubnet, AddrSubnetEither, EntryDest, EntryEither, IpStateBuilder, Subnet, SubnetEither, |
| }; |
| pub use crate::transport::udp::UdpEventDispatcher; |
| pub use crate::transport::TransportLayerEventDispatcher; |
| |
| use std::time; |
| |
| use crate::device::{DeviceLayerState, DeviceLayerTimerId}; |
| use crate::ip::{IpLayerState, IpLayerTimerId}; |
| use crate::transport::{TransportLayerState, TransportLayerTimerId}; |
| |
| /// Map an expression over either version of an address. |
| /// |
| /// `map_addr_version!` takes a type which is an enum with two variants - `V4` |
| /// and `V6` - and a value of that type. It matches on the variants, and for |
| /// both variants, invokes an expression on the inner contents. `$addr` is both |
| /// the name of the variable to match on, and the name that the address will be |
| /// bound to for the scope of the expression. |
| /// |
| /// To make it concrete, the expression `map_addr_version!(Foo, bar, blah(bar))` |
| /// desugars to: |
| /// |
| /// ```rust,ignore |
| /// match bar { |
| /// Foo::V4(bar) => blah(bar), |
| /// Foo::V6(bar) => blah(bar), |
| /// } |
| /// ``` |
| #[macro_export] |
| macro_rules! map_addr_version { |
| ($ty:tt, $addr:ident, $expr:expr) => { |
| match $addr { |
| $ty::V4($addr) => $expr, |
| $ty::V6($addr) => $expr, |
| } |
| }; |
| ($ty:tt, $addr:ident, $expr:expr,) => { |
| map_addr_version!($addr, $expr) |
| }; |
| } |
| |
| /// A builder for [`StackState`]. |
| #[derive(Default)] |
| pub struct StackStateBuilder { |
| ip: IpStateBuilder, |
| } |
| |
| impl StackStateBuilder { |
| /// Get the builder for the IP state. |
| pub fn ip_builder(&mut self) -> &mut IpStateBuilder { |
| &mut self.ip |
| } |
| |
| /// Consume this builder and produce a `StackState`. |
| pub fn build<D: EventDispatcher>(self) -> StackState<D> { |
| StackState { |
| transport: TransportLayerState::default(), |
| ip: self.ip.build(), |
| device: DeviceLayerState::default(), |
| #[cfg(test)] |
| test_counters: testutil::TestCounters::default(), |
| } |
| } |
| } |
| |
| /// The state associated with the network stack. |
| pub struct StackState<D: EventDispatcher> { |
| transport: TransportLayerState<D>, |
| ip: IpLayerState, |
| device: DeviceLayerState, |
| #[cfg(test)] |
| test_counters: testutil::TestCounters, |
| } |
| |
| impl<D: EventDispatcher> Default for StackState<D> { |
| fn default() -> StackState<D> { |
| StackStateBuilder::default().build() |
| } |
| } |
| |
| impl<D: EventDispatcher> StackState<D> { |
| /// Add a new ethernet device to the device layer. |
| pub fn add_ethernet_device(&mut self, mac: Mac, mtu: u32) -> DeviceId { |
| self.device.add_ethernet_device(mac, mtu) |
| } |
| } |
| |
| /// Context available during the execution of the netstack. |
| /// |
| /// `Context` provides access to the state of the netstack and to an event |
| /// dispatcher which can be used to emit events and schedule timeouts. A mutable |
| /// reference to a `Context` is passed to every function in the netstack. |
| #[derive(Default)] |
| pub struct Context<D: EventDispatcher> { |
| state: StackState<D>, |
| dispatcher: D, |
| } |
| |
| impl<D: EventDispatcher> Context<D> { |
| /// Construct a new `Context`. |
| pub fn new(state: StackState<D>, dispatcher: D) -> Context<D> { |
| Context { state, dispatcher } |
| } |
| |
| /// Construct a new `Context` using the default `StackState`. |
| pub fn with_default_state(dispatcher: D) -> Context<D> { |
| Context { state: StackState::default(), dispatcher } |
| } |
| |
| /// Get the stack state immutably. |
| pub fn state(&self) -> &StackState<D> { |
| &self.state |
| } |
| |
| /// Get the stack state mutably. |
| pub fn state_mut(&mut self) -> &mut StackState<D> { |
| &mut self.state |
| } |
| |
| /// Get the dispatcher. |
| pub fn dispatcher(&mut self) -> &mut D { |
| &mut self.dispatcher |
| } |
| |
| /// Get the stack state and the dispatcher. |
| /// |
| /// This is useful when a mutable reference to both are required at the same |
| /// time, which isn't possible when using the `state` or `dispatcher` |
| /// methods. |
| pub fn state_and_dispatcher(&mut self) -> (&mut StackState<D>, &mut D) { |
| (&mut self.state, &mut self.dispatcher) |
| } |
| } |
| |
| impl<D: EventDispatcher + Default> Context<D> { |
| /// Construct a new `Context` using the default dispatcher. |
| pub fn with_default_dispatcher(state: StackState<D>) -> Context<D> { |
| Context { state, dispatcher: D::default() } |
| } |
| } |
| |
| /// The identifier for any timer event. |
| #[derive(Copy, Clone, PartialEq, Debug)] |
| pub struct TimerId(TimerIdInner); |
| |
| #[derive(Copy, Clone, PartialEq, Debug)] |
| enum TimerIdInner { |
| /// A timer event in the device layer. |
| DeviceLayer(DeviceLayerTimerId), |
| /// A timer event in the transport layer. |
| TransportLayer(TransportLayerTimerId), |
| /// A timer event in the IP layer. |
| IpLayer(IpLayerTimerId), |
| /// A no-op timer event (used for tests) |
| #[cfg(test)] |
| Nop(usize), |
| } |
| |
| /// Handle a generic timer event. |
| pub fn handle_timeout<D: EventDispatcher>(ctx: &mut Context<D>, id: TimerId) { |
| match id { |
| TimerId(TimerIdInner::DeviceLayer(x)) => { |
| device::handle_timeout(ctx, x); |
| } |
| TimerId(TimerIdInner::TransportLayer(x)) => { |
| transport::handle_timeout(ctx, x); |
| } |
| TimerId(TimerIdInner::IpLayer(x)) => { |
| ip::handle_timeout(ctx, x); |
| } |
| #[cfg(test)] |
| TimerId(TimerIdInner::Nop(_)) => { |
| increment_counter!(ctx, "timer::nop"); |
| } |
| } |
| } |
| |
| /// A type representing an instant in time. |
| /// |
| /// `Instant` can be implemented by any type which represents an instant in |
| /// time. This can include any sort of real-world clock time (e.g., |
| /// [`std::time::Instant`]) or fake time such as in testing. |
| pub trait Instant: Sized { |
| /// Returns the amount of time elapsed from another instant to this one. |
| /// |
| /// # Panics |
| /// |
| /// This function will panic if `earlier` is later than `self`. |
| fn duration_since(&self, earlier: Self) -> time::Duration; |
| |
| /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be |
| /// represented as `Instant` (which means it's inside the bounds of the |
| /// underlying data structure), `None` otherwise. |
| fn checked_add(&self, duration: time::Duration) -> Option<Self>; |
| |
| /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be |
| /// represented as `Instant` (which means it's inside the bounds of the |
| /// underlying data structure), `None` otherwise. |
| fn checked_sub(&self, duration: time::Duration) -> Option<Self>; |
| } |
| |
| impl Instant for time::Instant { |
| fn duration_since(&self, earlier: time::Instant) -> time::Duration { |
| time::Instant::duration_since(self, earlier) |
| } |
| |
| fn checked_add(&self, duration: time::Duration) -> Option<Self> { |
| time::Instant::checked_add(self, duration) |
| } |
| |
| fn checked_sub(&self, duration: time::Duration) -> Option<Self> { |
| time::Instant::checked_sub(self, duration) |
| } |
| } |
| |
| /// An object which can dispatch events to a real system. |
| /// |
| /// An `EventDispatcher` provides access to a real system. It provides the |
| /// ability to emit events and schedule timeouts. Each layer of the stack |
| /// provides its own event dispatcher trait which specifies the types of actions |
| /// that must be supported in order to support that layer of the stack. The |
| /// `EventDispatcher` trait is a sub-trait of all of these traits. |
| pub trait EventDispatcher: DeviceLayerEventDispatcher + TransportLayerEventDispatcher { |
| /// The type of an instant in time. |
| /// |
| /// All time is measured using `Instant`s, including scheduling timeouts. |
| /// This type may represent some sort of real-world time (e.g., |
| /// [`std::time::Instant`]), or may be mocked in testing using a fake clock. |
| type Instant: Instant; |
| |
| /// Returns the current instant. |
| fn now(&self) -> Self::Instant; |
| |
| /// Schedule a callback to be invoked after a timeout. |
| /// |
| /// `schedule_timeout` schedules `f` to be invoked after `duration` has |
| /// elapsed, overwriting any previous timeout with the same ID. |
| /// |
| /// If there was previously a timer with that ID, return the time at which |
| /// is was scheduled to fire. |
| /// |
| /// # Panics |
| /// |
| /// `schedule_timeout` may panic if `duration` is large enough that |
| /// `self.now() + duration` overflows. |
| fn schedule_timeout(&mut self, duration: time::Duration, id: TimerId) -> Option<Self::Instant> { |
| self.schedule_timeout_instant(self.now().checked_add(duration).unwrap(), id) |
| } |
| |
| /// Schedule a callback to be invoked at a specific time. |
| /// |
| /// `schedule_timeout_instant` schedules `f` to be invoked at `time`, |
| /// overwriting any previous timeout with the same ID. |
| /// |
| /// If there was previously a timer with that ID, return the time at which |
| /// is was scheduled to fire. |
| fn schedule_timeout_instant( |
| &mut self, |
| time: Self::Instant, |
| id: TimerId, |
| ) -> Option<Self::Instant>; |
| |
| /// Cancel a timeout. |
| /// |
| /// Returns true if the timeout was cancelled, false if there was no timeout |
| /// for the given ID. |
| fn cancel_timeout(&mut self, id: TimerId) -> Option<Self::Instant>; |
| } |
| |
| /// Set the IP address and subnet for a device. |
| pub fn set_ip_addr_subnet<D: EventDispatcher>( |
| ctx: &mut Context<D>, |
| device: DeviceId, |
| addr_sub: AddrSubnetEither, |
| ) { |
| map_addr_version!( |
| AddrSubnetEither, |
| addr_sub, |
| crate::device::set_ip_addr_subnet(ctx, device, addr_sub) |
| ); |
| } |
| |
| /// Add a route to send all packets addressed to a specific subnet to a specific device. |
| pub fn add_device_route<D: EventDispatcher>( |
| ctx: &mut Context<D>, |
| subnet: SubnetEither, |
| device: DeviceId, |
| ) -> Result<(), error::NetstackError> { |
| map_addr_version!(SubnetEither, subnet, crate::ip::add_device_route(ctx, subnet, device)) |
| .map_err(From::from) |
| } |
| |
| /// Delete a route from the forwarding table, returning `Err` if no |
| /// route was found to be deleted. |
| pub fn del_device_route<D: EventDispatcher>( |
| ctx: &mut Context<D>, |
| subnet: SubnetEither, |
| ) -> Result<(), error::NetstackError> { |
| map_addr_version!(SubnetEither, subnet, crate::ip::del_device_route(ctx, subnet)) |
| .map_err(From::from) |
| } |
| |
| /// Get all the routes. |
| pub fn get_all_routes<'a, D: EventDispatcher>( |
| ctx: &'a Context<D>, |
| ) -> impl 'a + Iterator<Item = EntryEither> { |
| let v4_routes = ip::iter_routes::<_, ip::Ipv4Addr>(ctx); |
| let v6_routes = ip::iter_routes::<_, ip::Ipv6Addr>(ctx); |
| v4_routes.cloned().map(From::from).chain(v6_routes.cloned().map(From::from)) |
| } |