blob: ff0c52cd3bf3bd4a275df02ce6cc82c890908947 [file] [log] [blame]
// 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.
#![feature(async_await, await_macro)]
#![feature(never_type)]
#![feature(specialization)]
#![feature(try_from)]
// In case we roll the toolchain and something we're using as a feature has been
// stabilized.
#![allow(stable_features)]
// We use repr(packed) in some places (particularly in the wire module) to
// create structs whose layout matches the layout of network packets on the
// wire. This ensures that the compiler will stop us from using repr(packed) in
// an unsound manner without using unsafe code.
#![deny(safe_packed_borrows)]
#![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)]
#[macro_use]
mod macros;
mod device;
mod error;
mod ip;
#[cfg(test)]
mod testutil;
mod transport;
mod wire;
pub mod types;
use crate::device::{ethernet::Mac, DeviceId, DeviceLayerEventDispatcher, DeviceLayerTimerId};
use crate::transport::{TransportLayerEventDispatcher, TransportLayerTimerId};
use crate::device::DeviceLayerState;
use crate::ip::IpLayerState;
use crate::transport::TransportLayerState;
/// 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> {
StackState {
transport: TransportLayerState::default(),
ip: IpLayerState::default(),
device: DeviceLayerState::default(),
#[cfg(test)]
test_counters: testutil::TestCounters::default(),
}
}
}
impl<D: EventDispatcher> StackState<D> {
/// Add a new ethernet device to the device layer.
pub fn add_ethernet_device(&mut self, mac: Mac) -> DeviceId {
self.device.add_ethernet_device(mac)
}
}
/// 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.
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 }
}
/// Get the stack state.
pub fn state(&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)
}
}
/// The identifier for any timer event.
#[derive(Copy, Clone, PartialEq)]
pub struct TimerId(TimerIdInner);
#[derive(Copy, Clone, PartialEq)]
enum TimerIdInner {
/// A timer event in the device layer.
DeviceLayer(DeviceLayerTimerId),
/// A timer event in the transport layer.
TransportLayer(TransportLayerTimerId),
}
/// 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);
}
}
}
/// 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 {
/// 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.
fn schedule_timeout(
&mut self, duration: std::time::Duration, id: TimerId,
) -> Option<std::time::Instant>;
/// 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: std::time::Instant, id: TimerId,
) -> Option<std::time::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<std::time::Instant>;
}
/// Set the IP address for a device.
// TODO(wesleyac): A real error type
pub fn set_ip_addr<D: EventDispatcher>(
ctx: &mut Context<D>, device: DeviceId, addr: std::net::IpAddr, subnet: types::Subnet,
) -> Result<(), ()> {
match (addr, subnet.addr(), subnet.prefix_len()) {
(std::net::IpAddr::V4(ip), std::net::IpAddr::V4(subnet_addr), prefix_len) => {
crate::device::set_ip_addr(
ctx,
device,
crate::ip::Ipv4Addr::new(ip.octets()),
crate::ip::Subnet::new(crate::ip::Ipv4Addr::new(subnet_addr.octets()), prefix_len),
);
Ok(())
}
(std::net::IpAddr::V6(ip), std::net::IpAddr::V6(subnet_addr), prefix_len) => {
crate::device::set_ip_addr(
ctx,
device,
crate::ip::Ipv6Addr::new(ip.octets()),
crate::ip::Subnet::new(crate::ip::Ipv6Addr::new(subnet_addr.octets()), prefix_len),
);
Ok(())
}
_ => Err(()),
}
}
/// Get the IP addresses for a device.
pub fn get_ip_addr<D: EventDispatcher>(
ctx: &mut Context<D>, device: DeviceId,
) -> (Option<std::net::Ipv4Addr>, Option<std::net::Ipv6Addr>) {
unimplemented!();
}
/// 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: types::Subnet, device: DeviceId,
) {
match subnet.addr() {
std::net::IpAddr::V4(addr) => {
ip::add_device_route(
ctx,
ip::Subnet::new(ip::Ipv4Addr::from(addr), subnet.prefix_len()),
device,
);
}
std::net::IpAddr::V6(addr) => {
ip::add_device_route(
ctx,
ip::Subnet::new(ip::Ipv6Addr::from(addr), subnet.prefix_len()),
device,
);
}
}
}