blob: a9ed24d8f3efd18253f9b06b11c9739f11988906 [file] [log] [blame] [edit]
// 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 transport layer.
//!
//! # Listeners and connections
//!
//! Some transport layer protocols (notably TCP and UDP) follow a common pattern
//! with respect to registering listeners and connections. There are some
//! subtleties here that are worth pointing out.
//!
//! ## Connections
//!
//! A connection has simpler semantics than a listener. It is bound to a single
//! local address and port and a single remote address and port. By virtue of
//! being bound to a local address, it is also bound to a local interface. This
//! means that, regardless of the entries in the forwarding table, all traffic
//! on that connection will always egress over the same interface. [^1] This also
//! means that, if the interface's address changes, any connections bound to it
//! are severed.
//!
//! ## Listeners
//!
//! A listener, on the other hand, can be bound to any number of local addresses
//! (although it is still always bound to a particular port). From the
//! perspective of this crate, there are two ways of registering a listener:
//! - By specifying one or more local addresses, the listener will be bound to
//! each of those local addresses.
//! - By specifying zero local addresses, the listener will be bound to all
//! addresses. These are referred to in our documentation as "wildcard
//! listeners".
//!
//! The algorithm for figuring out what listener to deliver a packet to is as
//! follows: If there is any listener bound to the specific local address and
//! port addressed in the packet, deliver the packet to that listener.
//! Otherwise, if there is a wildcard listener bound the port addressed in the
//! packet, deliver the packet to that listener. This implies that if a listener
//! is removed which was bound to a particular local address, it can "uncover" a
//! wildcard listener bound to the same port, allowing traffic which would
//! previously have been delivered to the normal listener to now be delivered to
//! the wildcard listener.
//!
//! If desired, clients of this crate can implement a different mechanism for
//! registering listeners on all local addresses - enumerate every local
//! address, and then specify all of the local addresses when registering the
//! listener. This approach will not support shadowing, as a different listener
//! binding to the same port will explicitly conflict with the existing
//! listener, and will thus be rejected. In other words, from the perspective of
//! this crate's API, such listeners will appear like normal listeners that just
//! happen to bind all of the addresses, rather than appearing like wildcard
//! listeners.
//!
//! [^1]: It is an open design question as to whether incoming traffic on the
//! connection will be accepted from a different interface. This is part
//! of the "weak host model" vs "strong host model" discussion.
pub(crate) mod udp;
use alloc::collections::HashMap;
use alloc::vec::Vec;
use core::hash::Hash;
use net_types::ip::{Ipv4, Ipv6};
use crate::data_structures::IdMap;
use crate::ip::icmp::IcmpIpExt;
use crate::transport::udp::{UdpEventDispatcher, UdpStateBuilder};
use crate::{Context, EventDispatcher};
/// A builder for transport layer state.
#[derive(Default, Clone)]
pub struct TransportStateBuilder {
udp: UdpStateBuilder,
}
impl TransportStateBuilder {
/// Get the builder for the UDP state.
pub fn udp_builder(&mut self) -> &mut UdpStateBuilder {
&mut self.udp
}
pub(crate) fn build(self) -> TransportLayerState {
TransportLayerState { udpv4: self.udp.clone().build(), udpv6: self.udp.build() }
}
}
/// The state associated with the transport layer.
pub(crate) struct TransportLayerState {
udpv4: self::udp::UdpState<Ipv4>,
udpv6: self::udp::UdpState<Ipv6>,
}
/// The identifier for timer events in the transport layer.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub(crate) enum TransportLayerTimerId {}
/// Handle a timer event firing in the transport layer.
pub(crate) fn handle_timeout<D: EventDispatcher>(_ctx: &mut Context<D>, id: TransportLayerTimerId) {
match id {}
}
/// An event dispatcher for the transport layer.
///
/// See the `EventDispatcher` trait in the crate root for more details.
pub trait TransportLayerEventDispatcher<I: IcmpIpExt>: UdpEventDispatcher<I> {}
/// A bidirectional map between listeners and addresses.
///
/// A `ListenerAddrMap` keeps addresses mapped by integer indexes, and allows
/// for constant-time mapping in either direction (though address -> index
/// mappings are via a hash map, and are thus slower).
struct ListenerAddrMap<A> {
listener_to_addrs: IdMap<Vec<A>>,
addr_to_listener: HashMap<A, usize>,
}
impl<A: Eq + Hash + Clone> ListenerAddrMap<A> {
fn insert(&mut self, addrs: Vec<A>) -> usize {
let listener = self.listener_to_addrs.push(addrs.clone());
for addr in &addrs {
self.addr_to_listener.insert(addr.clone(), listener);
}
listener
}
}
impl<A: Eq + Hash> ListenerAddrMap<A> {
fn get_by_addr(&self, addr: &A) -> Option<usize> {
self.addr_to_listener.get(addr).cloned()
}
fn get_by_listener(&self, listener: usize) -> Option<&Vec<A>> {
self.listener_to_addrs.get(listener)
}
fn remove_by_listener(&mut self, listener: usize) -> Option<Vec<A>> {
let addrs = self.listener_to_addrs.remove(listener)?;
for addr in &addrs {
self.addr_to_listener.remove(addr).unwrap();
}
Some(addrs)
}
}
impl<A: Eq + Hash> Default for ListenerAddrMap<A> {
fn default() -> ListenerAddrMap<A> {
ListenerAddrMap {
listener_to_addrs: IdMap::default(),
addr_to_listener: HashMap::default(),
}
}
}
/// A bidirectional map between connections and addresses.
///
/// A `ConnAddrMap` keeps addresses mapped by integer indexes, and allows for
/// constant-time mapping in either direction (though address -> index mappings
/// are via a hash map, and thus slower).
///
/// It differs from a `ListenerAddrMap` in that only a single address per
/// connection is supported.
pub(crate) struct ConnAddrMap<A, C = A> {
id_to_conn: IdMap<C>,
addr_to_id: HashMap<A, usize>,
}
impl<A: Eq + Hash + Clone, C> ConnAddrMap<A, C> {
pub(crate) fn insert(&mut self, addr: A, conn: C) -> usize {
let id = self.id_to_conn.push(conn);
self.addr_to_id.insert(addr, id);
id
}
}
impl<A: Eq + Hash, C> ConnAddrMap<A, C> {
pub(crate) fn get_id_by_addr(&self, addr: &A) -> Option<usize> {
self.addr_to_id.get(addr).cloned()
}
pub(crate) fn get_conn_by_id(&self, id: usize) -> Option<&C> {
self.id_to_conn.get(id)
}
}
impl<A: Eq + Hash, C> ConnAddrMap<A, C>
where
for<'a> &'a C: Into<A>,
{
fn remove_by_id(&mut self, id: usize) -> Option<C> {
let conn = self.id_to_conn.remove(id)?;
self.addr_to_id.remove(&(&conn).into()).unwrap();
Some(conn)
}
/// Update the elements of the map in-place, retaining only the elements for
/// which `f` returns `Ok`.
///
/// `update_retain` has the same behavior as [`IdMap::update_retain`]; see
/// its documentation for details.
///
/// [`IdMap::update_retain`]: crate::data_structures::IdMap::update_retain
pub(crate) fn update_retain<'a, E: 'a, F: 'a + Fn(&mut C) -> Result<(), E>>(
&'a mut self,
f: F,
) -> impl 'a + Iterator<Item = (usize, C, E)> {
let addr_to_id = &mut self.addr_to_id;
self.id_to_conn.update_retain(f).map(move |(id, conn, err)| {
addr_to_id.remove(&(&conn).into()).unwrap();
(id, conn, err)
})
}
}
impl<A: Eq + Hash, C> Default for ConnAddrMap<A, C> {
fn default() -> ConnAddrMap<A, C> {
ConnAddrMap { id_to_conn: IdMap::default(), addr_to_id: HashMap::default() }
}
}