blob: a75738c5a17334f72e75fc2f9144b5f65c42b4ae [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.
//! The device layer.
pub mod arp;
pub mod ethernet;
use std::collections::HashMap;
use std::fmt::{self, Debug, Display, Formatter};
use log::debug;
use packet::Serializer;
use crate::device::ethernet::{EthernetDeviceState, Mac};
use crate::ip::{IpAddr, Ipv4Addr, Subnet};
use crate::{Context, EventDispatcher};
/// An ID identifying a device.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct DeviceId {
id: u64,
protocol: DeviceProtocol,
}
impl DeviceId {
fn new_ethernet(id: u64) -> DeviceId {
DeviceId {
id,
protocol: DeviceProtocol::Ethernet,
}
}
#[allow(missing_docs)]
pub fn id(&self) -> u64 {
self.id
}
}
impl Display for DeviceId {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}:{}", self.protocol, self.id)
}
}
impl Debug for DeviceId {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
Display::fmt(self, f)
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum DeviceProtocol {
Ethernet,
}
impl Display for DeviceProtocol {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{}",
match self {
DeviceProtocol::Ethernet => "Ethernet",
}
)
}
}
/// The state associated with the device layer.
pub struct DeviceLayerState {
// Invariant: even though each protocol has its own hash map, IDs (used as
// keys in the hash maps) are unique across all hash maps. This is
// guaranteed by allocating IDs sequentially, and never re-using an ID.
next_id: u64,
ethernet: HashMap<u64, EthernetDeviceState>,
}
impl DeviceLayerState {
/// Add a new ethernet device to the device layer.
pub fn add_ethernet_device(&mut self, mac: Mac) -> DeviceId {
let id = self.allocate_id();
self.ethernet.insert(id, EthernetDeviceState::new(mac));
debug!("adding Ethernet device with ID {}", id);
DeviceId::new_ethernet(id)
}
fn allocate_id(&mut self) -> u64 {
let id = self.next_id;
self.next_id += 1;
id
}
}
// `next_id` starts at 1 for compatiblity with the fuchsia.net.stack.Stack
// interface, which does not allow for IDs of zero.
impl Default for DeviceLayerState {
fn default() -> DeviceLayerState {
DeviceLayerState {
next_id: 1,
ethernet: HashMap::new(),
}
}
}
/// The identifier for timer events in the device layer.
#[derive(Copy, Clone, PartialEq)]
pub enum DeviceLayerTimerId {
/// A timer event in the ARP layer with a protocol type of IPv4
ArpIpv4(arp::ArpTimerId<Ipv4Addr>),
}
/// Handle a timer event firing in the device layer.
pub fn handle_timeout<D: EventDispatcher>(ctx: &mut Context<D>, id: DeviceLayerTimerId) {
match id {
DeviceLayerTimerId::ArpIpv4(inner_id) => arp::handle_timeout(ctx, inner_id),
}
}
/// An event dispatcher for the device layer.
///
/// See the `EventDispatcher` trait in the crate root for more details.
pub trait DeviceLayerEventDispatcher {
/// Send a frame to a device driver.
fn send_frame(&mut self, device: DeviceId, frame: &[u8]);
}
/// Send an IP packet in a device layer frame.
///
/// `send_ip_frame` accepts a device ID, a local IP address, and a
/// `SerializationRequest`. It computes the routing information and serializes
/// the request in a new device layer frame and sends it.
pub fn send_ip_frame<D: EventDispatcher, A, S>(
ctx: &mut Context<D>, device: DeviceId, local_addr: A, body: S,
) where
A: IpAddr,
S: Serializer,
{
match device.protocol {
DeviceProtocol::Ethernet => self::ethernet::send_ip_frame(ctx, device.id, local_addr, body),
}
}
/// Receive a device layer frame from the network.
pub fn receive_frame<D: EventDispatcher>(ctx: &mut Context<D>, device: DeviceId, bytes: &mut [u8]) {
match device.protocol {
DeviceProtocol::Ethernet => self::ethernet::receive_frame(ctx, device.id, bytes),
}
}
/// Get the IP address and subnet associated with this device.
pub fn get_ip_addr<D: EventDispatcher, A: IpAddr>(
ctx: &mut Context<D>, device: DeviceId,
) -> Option<(A, Subnet<A>)> {
match device.protocol {
DeviceProtocol::Ethernet => self::ethernet::get_ip_addr(ctx, device.id),
}
}
/// Set the IP address and subnet associated with this device.
pub fn set_ip_addr<D: EventDispatcher, A: IpAddr>(
ctx: &mut Context<D>, device: DeviceId, addr: A, subnet: Subnet<A>,
) {
assert!(subnet.contains(addr));
match device.protocol {
DeviceProtocol::Ethernet => self::ethernet::set_ip_addr(ctx, device.id, addr, subnet),
}
}