blob: dd8368cc09c5de4d36ce7db4cc1162f619277176 [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.
//! Common types for dealing with ip table entries.
use core::{
convert::Infallible as Never,
fmt::{Debug, Display, Formatter},
hash::Hash,
};
use net_types::{
ip::{GenericOverIp, Ip, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Subnet, SubnetEither},
SpecifiedAddr,
};
use crate::socket::address::SocketIpAddr;
/// The priority of a forwarding entry. Lower metrics are preferred.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct RawMetric(pub u32);
impl Display for RawMetric {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
let RawMetric(metric) = self;
write!(f, "{}", metric)
}
}
impl From<RawMetric> for u32 {
fn from(RawMetric(metric): RawMetric) -> u32 {
metric
}
}
impl From<RawMetric> for u64 {
fn from(RawMetric(metric): RawMetric) -> u64 {
u64::from(metric)
}
}
/// The metric for an [`AddableEntry`].
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum AddableMetric {
/// The entry's metric is unspecified, and shall defer to the routing metric
/// of its interface.
MetricTracksInterface,
/// The entry's metric shall be the following explicit value.
ExplicitMetric(RawMetric),
}
impl From<Metric> for AddableMetric {
fn from(metric: Metric) -> AddableMetric {
match metric {
Metric::MetricTracksInterface(_) => AddableMetric::MetricTracksInterface,
Metric::ExplicitMetric(metric) => AddableMetric::ExplicitMetric(metric),
}
}
}
/// `AddableEntry` is a routing entry that may be used to add a new entry to the
/// forwarding table.
///
/// See [`Entry`] for the type used to represent a route in the forwarding
/// table.
#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq, Hash)]
#[generic_over_ip(A, IpAddress)]
pub struct AddableEntry<A: IpAddress, D> {
/// The destination subnet.
pub subnet: Subnet<A>,
/// The outgoing interface.
pub device: D,
/// Next hop.
pub gateway: Option<SpecifiedAddr<A>>,
/// Route metric.
pub metric: AddableMetric,
}
impl<D, A: IpAddress> AddableEntry<A, D> {
/// Creates a new [`AddableEntry`] with a specified gateway.
pub fn with_gateway(
subnet: Subnet<A>,
device: D,
gateway: SpecifiedAddr<A>,
metric: AddableMetric,
) -> Self {
Self { subnet, device, gateway: Some(gateway), metric }
}
/// Creates a new [`AddableEntry`] with a specified device.
pub fn without_gateway(subnet: Subnet<A>, device: D, metric: AddableMetric) -> Self {
Self { subnet, device, gateway: None, metric }
}
/// Converts the `AddableEntry` to an `Entry`.
pub fn resolve_metric(self, device_metric: RawMetric) -> Entry<A, D> {
let Self { subnet, device, gateway, metric } = self;
let metric = match metric {
AddableMetric::MetricTracksInterface => Metric::MetricTracksInterface(device_metric),
AddableMetric::ExplicitMetric(metric) => Metric::ExplicitMetric(metric),
};
Entry { subnet, device, gateway, metric }
}
/// Maps the device ID held by this `AddableEntry`.
pub fn map_device_id<D2>(self, f: impl FnOnce(D) -> D2) -> AddableEntry<A, D2> {
let Self { subnet, device, gateway, metric } = self;
AddableEntry { subnet, device: f(device), gateway, metric }
}
/// Fallibly maps the device ID held by this `AddableEntry`.
pub fn try_map_device_id<D2, E>(
self,
f: impl FnOnce(D) -> Result<D2, E>,
) -> Result<AddableEntry<A, D2>, E> {
let Self { subnet, device, gateway, metric } = self;
Ok(AddableEntry { subnet, device: f(device)?, gateway, metric })
}
/// Sets the generation on an entry.
pub fn with_generation(self, generation: Generation) -> AddableEntryAndGeneration<A, D> {
AddableEntryAndGeneration { entry: self, generation }
}
}
/// An IPv4 forwarding entry or an IPv6 forwarding entry.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq, Hash)]
#[generic_over_ip()]
pub enum AddableEntryEither<D> {
V4(AddableEntry<Ipv4Addr, D>),
V6(AddableEntry<Ipv6Addr, D>),
}
impl<D> AddableEntryEither<D> {
/// Creates a new [`AddableEntryEither`] with the specified device as the
/// next hop.
pub fn without_gateway(subnet: SubnetEither, device: D, metric: AddableMetric) -> Self {
match subnet {
SubnetEither::V4(subnet) => {
AddableEntry::without_gateway(subnet, device, metric).into()
}
SubnetEither::V6(subnet) => {
AddableEntry::without_gateway(subnet, device, metric).into()
}
}
}
}
impl<A: IpAddress, D> From<AddableEntry<A, D>> for AddableEntryEither<D> {
fn from(entry: AddableEntry<A, D>) -> AddableEntryEither<D> {
A::Version::map_ip(entry, AddableEntryEither::V4, AddableEntryEither::V6)
}
}
/// A routing table entry together with the generation it was created in.
#[derive(Debug, Copy, Clone, GenericOverIp)]
#[generic_over_ip(A, IpAddress)]
pub struct AddableEntryAndGeneration<A: IpAddress, D> {
/// The entry.
pub entry: AddableEntry<A, D>,
/// The generation in which it was created.
pub generation: Generation,
}
/// Wraps a callback to upgrade a "stored" entry to a "live" entry.
#[derive(GenericOverIp)]
#[generic_over_ip(A, IpAddress)]
pub struct EntryUpgrader<'a, A: IpAddress, DeviceId, WeakDeviceId>(
// TODO(https://fxbug.dev/42148629): Remove or explain #[allow(dead_code)].
#[allow(dead_code)]
pub &'a mut dyn FnMut(
AddableEntryAndGeneration<A, WeakDeviceId>,
) -> Option<EntryAndGeneration<A, DeviceId>>,
);
impl<A: IpAddress, D> From<Entry<A, D>> for AddableEntry<A, D> {
fn from(Entry { subnet, device, gateway, metric }: Entry<A, D>) -> Self {
Self { subnet: subnet, device: device, gateway: gateway, metric: metric.into() }
}
}
/// The metric for an [`Entry`].
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Metric {
/// The entry's metric tracks its interface's routing metric and has the
/// included value.
MetricTracksInterface(RawMetric),
/// The entry's metric was explicitly set to the included value.
ExplicitMetric(RawMetric),
}
impl Metric {
/// Returns the underlying value of the `Metric`.
pub fn value(&self) -> RawMetric {
match self {
Self::MetricTracksInterface(value) => *value,
Self::ExplicitMetric(value) => *value,
}
}
}
/// A forwarding entry.
///
/// `Entry` is a `Subnet` with an egress device and optional gateway.
#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq, Hash)]
#[generic_over_ip(A, IpAddress)]
pub struct Entry<A: IpAddress, D> {
/// The matching subnet.
pub subnet: Subnet<A>,
/// The destination device.
pub device: D,
/// An optional gateway if the subnet is not on link.
// TODO(https://fxbug.dev/42074188): Restrict `gateway` to `UnicastAddr`.
pub gateway: Option<SpecifiedAddr<A>>,
/// The metric of the entry.
pub metric: Metric,
}
/// A forwarding entry with the generation it was created in.
#[derive(Debug, Copy, Clone, GenericOverIp, PartialEq, Eq)]
#[generic_over_ip(A, IpAddress)]
pub struct EntryAndGeneration<A: IpAddress, D> {
/// The entry.
pub entry: Entry<A, D>,
/// The generation.
pub generation: Generation,
}
impl<A: IpAddress, D: Debug> Display for EntryAndGeneration<A, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
let EntryAndGeneration { entry, generation: Generation(generation) } = self;
write!(f, "{} (generation = {})", entry, generation)
}
}
/// Used to compare routes for how early they were added to the table.
///
/// If two routes have the same prefix length and metric, and are both on-link
/// or are both-off-link, then the route with the earlier generation will be
/// preferred.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Generation(u64);
impl Generation {
/// Returns the initial generation.
pub fn initial() -> Self {
Self(0)
}
/// Returns the next generation.
pub fn next(&self) -> Generation {
let Self(n) = self;
Generation(n + 1)
}
}
impl<A: IpAddress, D> Entry<A, D> {
/// Maps the device ID held by this `Entry`.
pub fn map_device_id<D2>(self, f: impl FnOnce(D) -> D2) -> Entry<A, D2> {
let Self { subnet, device, gateway, metric } = self;
Entry { subnet, device: f(device), gateway, metric }
}
/// Sets the generation on an entry.
pub fn with_generation(self, generation: Generation) -> EntryAndGeneration<A, D> {
EntryAndGeneration { entry: self, generation }
}
}
impl<A: IpAddress, D: Debug> Display for Entry<A, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
let Entry { subnet, device, gateway, metric } = self;
match gateway {
Some(gateway) => {
write!(f, "{:?} (via {}) -> {} metric {}", device, gateway, subnet, metric.value())
}
None => write!(f, "{:?} -> {} metric {}", device, subnet, metric.value()),
}
}
}
/// An IPv4 forwarding entry or an IPv6 forwarding entry.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq)]
#[generic_over_ip()]
pub enum EntryEither<D> {
V4(Entry<Ipv4Addr, D>),
V6(Entry<Ipv6Addr, D>),
}
impl<A: IpAddress, D> From<Entry<A, D>> for EntryEither<D> {
fn from(entry: Entry<A, D>) -> EntryEither<D> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct EntryHolder<I: Ip, D>(Entry<I::Addr, D>);
A::Version::map_ip(entry, EntryEither::V4, EntryEither::V6)
}
}
/// `OrderedLocality` provides an implementation of `core::cmp::PartialOrd` for
/// a route's "locality".
// Define an enum, so that `OnLink` routes are sorted before `OffLink` routes.
// See https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#derivable for
// more details.
#[derive(PartialEq, PartialOrd, Eq, Ord)]
pub(crate) enum OrderedLocality {
// The route does not have a gateway.
OnLink,
// The route does have a gateway.
OffLink,
}
// `OrderedRoute` provides an implementation of `core::cmp::PartialOrd`
// for routes. Note that the fields are consulted in the order they are
// declared. For more details, see
// https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#derivable.
#[derive(PartialEq, PartialOrd, Eq, Ord)]
pub(crate) struct OrderedEntry<'a, A: IpAddress, D> {
// Order longer prefixes before shorter prefixes.
prefix_len: core::cmp::Reverse<u8>,
// Order lower metrics before larger metrics.
metric: u32,
// Order `OnLink` routes before `OffLink` routes.
locality: OrderedLocality,
// Earlier-added routes should come before later ones.
generation: Generation,
// To provide a consistent ordering, tiebreak using the remaining fields
// of the entry.
subnet_addr: A,
device: &'a D,
// Note that while this appears to duplicate the ordering provided by
// `locality`, it's important that we sort above on presence of the gateway
// and not on the actual address of the gateway. The latter is only used
// for tiebreaking at the end to provide a total order. Duplicating it this
// way allows us to avoid writing a manual `PartialOrd` impl.
gateway: Option<SpecifiedAddr<A>>,
}
impl<'a, A: IpAddress, D> From<&'a EntryAndGeneration<A, D>> for OrderedEntry<'a, A, D> {
fn from(entry: &'a EntryAndGeneration<A, D>) -> OrderedEntry<'a, A, D> {
let EntryAndGeneration { entry: Entry { subnet, device, gateway, metric }, generation } =
entry;
OrderedEntry {
prefix_len: core::cmp::Reverse(subnet.prefix()),
metric: metric.value().into(),
locality: gateway.map_or(OrderedLocality::OnLink, |_gateway| OrderedLocality::OffLink),
generation: *generation,
subnet_addr: subnet.network(),
device: &device,
gateway: *gateway,
}
}
}
/// `Ip` extension trait to assist in defining [`NextHop`].
pub trait IpTypesIpExt: packet_formats::ip::IpExt {
/// A marker type carried by the [`NextHop::Broadcast`] variant to indicate
/// that it is uninhabited for IPv6.
type BroadcastMarker: Debug + Copy + Clone + PartialEq + Eq;
}
impl IpTypesIpExt for Ipv4 {
type BroadcastMarker = ();
}
impl IpTypesIpExt for Ipv6 {
type BroadcastMarker = Never;
}
/// Wrapper struct to provide a convenient [`GenericOverIp`] impl for use
/// with [`IpTypesIpExt::BroadcastMarker`].
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
pub struct WrapBroadcastMarker<I: IpTypesIpExt>(pub I::BroadcastMarker);
/// The next hop for a [`Destination`].
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NextHop<A: IpAddress>
where
A::Version: IpTypesIpExt,
{
/// Indicates that the next-hop for a the packet is the remote since it is a
/// neighboring node (on-link).
RemoteAsNeighbor,
/// Indicates that the next-hop is a gateway/router since the remote is not
/// a neighboring node (off-link).
Gateway(SpecifiedAddr<A>),
/// Indicates that the packet should be broadcast rather than sent to a
/// specific neighbor.
Broadcast(<A::Version as IpTypesIpExt>::BroadcastMarker),
}
impl<A: IpAddress, NewIp: IpTypesIpExt> GenericOverIp<NewIp> for NextHop<A>
where
A::Version: IpTypesIpExt,
{
type Type = NextHop<NewIp::Addr>;
}
impl<A> NextHop<A>
where
A: IpAddress,
A::Version: IpTypesIpExt,
{
pub(crate) fn into_next_hop_and_broadcast_marker(
self,
remote_ip: SpecifiedAddr<A>,
) -> (SpecifiedAddr<A>, Option<<A::Version as IpTypesIpExt>::BroadcastMarker>) {
match self {
NextHop::RemoteAsNeighbor => (remote_ip, None),
NextHop::Gateway(gateway) => (gateway, None),
NextHop::Broadcast(marker) => (remote_ip, Some(marker)),
}
}
}
/// An IP Address that witnesses properties needed to be routed.
pub type RoutableIpAddr<A> = SocketIpAddr<A>;
/// The resolved route to a destination IP address.
#[derive(Debug, Copy, Clone, PartialEq, Eq, GenericOverIp)]
#[generic_over_ip(I, Ip)]
pub struct ResolvedRoute<I: IpTypesIpExt, D> {
/// The source address to use when forwarding packets towards the
/// destination.
pub src_addr: RoutableIpAddr<I::Addr>,
/// The device over which this destination can be reached.
pub device: D,
/// Present when `device` is loopback with the device that the destination
/// address is assigned to.
///
/// NB: it's possible that `local_delivery_device` is itself loopback.
pub local_delivery_device: Option<D>,
/// The next hop via which this destination can be reached.
pub next_hop: NextHop<I::Addr>,
}
/// The destination of an outbound IP packet.
///
/// Outbound IP packets are sent to a particular device (specified by the
/// `device` field).
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) struct Destination<A: IpAddress, D>
where
A::Version: IpTypesIpExt,
{
/// Indicates the next hop via which this destination can be reached.
pub(crate) next_hop: NextHop<A>,
/// Indicates the device over which this destination can be reached.
pub(crate) device: D,
}