netstack3_ip/
types.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Common types for dealing with ip table entries.
6
7use core::fmt::{Debug, Display, Formatter};
8use core::hash::Hash;
9
10use net_types::ip::{GenericOverIp, Ip, IpAddress, Ipv4Addr, Ipv6Addr, Subnet, SubnetEither};
11use net_types::SpecifiedAddr;
12use netstack3_base::socket::SocketIpAddr;
13use netstack3_base::{BroadcastIpExt, IpDeviceAddr};
14
15/// The priority of a forwarding entry. Lower metrics are preferred.
16#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
17pub struct RawMetric(pub u32);
18
19impl RawMetric {
20    /// The highest possible preference for a metric value, i.e., the lowest
21    /// numerical value.
22    pub const HIGHEST_PREFERENCE: Self = Self(u32::MIN);
23    /// The lowest possible preference for a metric value, i.e., the highest
24    /// numerical value.
25    pub const LOWEST_PREFERENCE: Self = Self(u32::MAX);
26}
27
28impl Display for RawMetric {
29    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
30        let RawMetric(metric) = self;
31        write!(f, "{}", metric)
32    }
33}
34
35impl From<RawMetric> for u32 {
36    fn from(RawMetric(metric): RawMetric) -> u32 {
37        metric
38    }
39}
40
41impl From<RawMetric> for u64 {
42    fn from(RawMetric(metric): RawMetric) -> u64 {
43        u64::from(metric)
44    }
45}
46
47/// The metric for an [`AddableEntry`].
48#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
49pub enum AddableMetric {
50    /// The entry's metric is unspecified, and shall defer to the routing metric
51    /// of its interface.
52    MetricTracksInterface,
53    /// The entry's metric shall be the following explicit value.
54    ExplicitMetric(RawMetric),
55}
56
57impl From<Metric> for AddableMetric {
58    fn from(metric: Metric) -> AddableMetric {
59        match metric {
60            Metric::MetricTracksInterface(_) => AddableMetric::MetricTracksInterface,
61            Metric::ExplicitMetric(metric) => AddableMetric::ExplicitMetric(metric),
62        }
63    }
64}
65
66/// `AddableEntry` is a routing entry that may be used to add a new entry to the
67/// forwarding table.
68///
69/// See [`Entry`] for the type used to represent a route in the forwarding
70/// table.
71#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq, Hash)]
72#[generic_over_ip(A, IpAddress)]
73pub struct AddableEntry<A: IpAddress, D> {
74    /// The destination subnet.
75    pub subnet: Subnet<A>,
76    /// The outgoing interface.
77    pub device: D,
78    /// Next hop.
79    pub gateway: Option<SpecifiedAddr<A>>,
80    /// Route metric.
81    pub metric: AddableMetric,
82}
83
84impl<D, A: IpAddress> AddableEntry<A, D> {
85    /// Creates a new [`AddableEntry`] with a specified gateway.
86    pub fn with_gateway(
87        subnet: Subnet<A>,
88        device: D,
89        gateway: SpecifiedAddr<A>,
90        metric: AddableMetric,
91    ) -> Self {
92        Self { subnet, device, gateway: Some(gateway), metric }
93    }
94
95    /// Creates a new [`AddableEntry`] with a specified device.
96    pub fn without_gateway(subnet: Subnet<A>, device: D, metric: AddableMetric) -> Self {
97        Self { subnet, device, gateway: None, metric }
98    }
99
100    /// Converts the `AddableEntry` to an `Entry`.
101    pub fn resolve_metric(self, device_metric: RawMetric) -> Entry<A, D> {
102        let Self { subnet, device, gateway, metric } = self;
103        let metric = match metric {
104            AddableMetric::MetricTracksInterface => Metric::MetricTracksInterface(device_metric),
105            AddableMetric::ExplicitMetric(metric) => Metric::ExplicitMetric(metric),
106        };
107        Entry { subnet, device, gateway, metric }
108    }
109
110    /// Maps the device ID held by this `AddableEntry`.
111    pub fn map_device_id<D2>(self, f: impl FnOnce(D) -> D2) -> AddableEntry<A, D2> {
112        let Self { subnet, device, gateway, metric } = self;
113        AddableEntry { subnet, device: f(device), gateway, metric }
114    }
115
116    /// Fallibly maps the device ID held by this `AddableEntry`.
117    pub fn try_map_device_id<D2, E>(
118        self,
119        f: impl FnOnce(D) -> Result<D2, E>,
120    ) -> Result<AddableEntry<A, D2>, E> {
121        let Self { subnet, device, gateway, metric } = self;
122        Ok(AddableEntry { subnet, device: f(device)?, gateway, metric })
123    }
124
125    /// Sets the generation on an entry.
126    pub fn with_generation(self, generation: Generation) -> AddableEntryAndGeneration<A, D> {
127        AddableEntryAndGeneration { entry: self, generation }
128    }
129}
130
131/// An IPv4 forwarding entry or an IPv6 forwarding entry.
132#[allow(missing_docs)]
133#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq, Hash)]
134#[generic_over_ip()]
135pub enum AddableEntryEither<D> {
136    V4(AddableEntry<Ipv4Addr, D>),
137    V6(AddableEntry<Ipv6Addr, D>),
138}
139
140impl<D> AddableEntryEither<D> {
141    /// Creates a new [`AddableEntryEither`] with the specified device as the
142    /// next hop.
143    pub fn without_gateway(subnet: SubnetEither, device: D, metric: AddableMetric) -> Self {
144        match subnet {
145            SubnetEither::V4(subnet) => {
146                AddableEntry::without_gateway(subnet, device, metric).into()
147            }
148            SubnetEither::V6(subnet) => {
149                AddableEntry::without_gateway(subnet, device, metric).into()
150            }
151        }
152    }
153}
154
155impl<A: IpAddress, D> From<AddableEntry<A, D>> for AddableEntryEither<D> {
156    fn from(entry: AddableEntry<A, D>) -> AddableEntryEither<D> {
157        A::Version::map_ip(entry, AddableEntryEither::V4, AddableEntryEither::V6)
158    }
159}
160
161/// A routing table entry together with the generation it was created in.
162#[derive(Debug, Copy, Clone, GenericOverIp)]
163#[generic_over_ip(A, IpAddress)]
164pub struct AddableEntryAndGeneration<A: IpAddress, D> {
165    /// The entry.
166    pub entry: AddableEntry<A, D>,
167    /// The generation in which it was created.
168    pub generation: Generation,
169}
170
171impl<A: IpAddress, D> From<Entry<A, D>> for AddableEntry<A, D> {
172    fn from(Entry { subnet, device, gateway, metric }: Entry<A, D>) -> Self {
173        Self { subnet: subnet, device: device, gateway: gateway, metric: metric.into() }
174    }
175}
176
177/// The metric for an [`Entry`].
178#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
179pub enum Metric {
180    /// The entry's metric tracks its interface's routing metric and has the
181    /// included value.
182    MetricTracksInterface(RawMetric),
183    /// The entry's metric was explicitly set to the included value.
184    ExplicitMetric(RawMetric),
185}
186
187impl Metric {
188    /// Returns the underlying value of the `Metric`.
189    pub fn value(&self) -> RawMetric {
190        match self {
191            Self::MetricTracksInterface(value) => *value,
192            Self::ExplicitMetric(value) => *value,
193        }
194    }
195}
196
197/// A forwarding entry.
198///
199/// `Entry` is a `Subnet` with an egress device and optional gateway.
200#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq, Hash)]
201#[generic_over_ip(A, IpAddress)]
202pub struct Entry<A: IpAddress, D> {
203    /// The matching subnet.
204    pub subnet: Subnet<A>,
205    /// The destination device.
206    pub device: D,
207    /// An optional gateway if the subnet is not on link.
208    // TODO(https://fxbug.dev/42074188): Restrict `gateway` to `UnicastAddr`.
209    pub gateway: Option<SpecifiedAddr<A>>,
210    /// The metric of the entry.
211    pub metric: Metric,
212}
213
214/// A forwarding entry with the generation it was created in.
215#[derive(Debug, Copy, Clone, GenericOverIp, PartialEq, Eq)]
216#[generic_over_ip(A, IpAddress)]
217pub struct EntryAndGeneration<A: IpAddress, D> {
218    /// The entry.
219    pub entry: Entry<A, D>,
220    /// The generation.
221    pub generation: Generation,
222}
223
224impl<A: IpAddress, D: Debug> Display for EntryAndGeneration<A, D> {
225    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
226        let EntryAndGeneration { entry, generation: Generation(generation) } = self;
227        write!(f, "{} (generation = {})", entry, generation)
228    }
229}
230
231/// Used to compare routes for how early they were added to the table.
232///
233/// If two routes have the same prefix length and metric, and are both on-link
234/// or are both-off-link, then the route with the earlier generation will be
235/// preferred.
236#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
237pub struct Generation(u64);
238
239impl Generation {
240    /// Returns the initial generation.
241    pub fn initial() -> Self {
242        Self(0)
243    }
244
245    /// Returns the next generation.
246    pub fn next(&self) -> Generation {
247        let Self(n) = self;
248        Generation(n + 1)
249    }
250}
251
252impl<A: IpAddress, D> Entry<A, D> {
253    /// Maps the device ID held by this `Entry`.
254    pub fn map_device_id<D2>(self, f: impl FnOnce(D) -> D2) -> Entry<A, D2> {
255        let Self { subnet, device, gateway, metric } = self;
256        Entry { subnet, device: f(device), gateway, metric }
257    }
258
259    /// Sets the generation on an entry.
260    pub fn with_generation(self, generation: Generation) -> EntryAndGeneration<A, D> {
261        EntryAndGeneration { entry: self, generation }
262    }
263}
264
265impl<A: IpAddress, D: Debug> Display for Entry<A, D> {
266    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
267        let Entry { subnet, device, gateway, metric } = self;
268        match gateway {
269            Some(gateway) => {
270                write!(f, "{:?} (via {}) -> {} metric {}", device, gateway, subnet, metric.value())
271            }
272            None => write!(f, "{:?} -> {} metric {}", device, subnet, metric.value()),
273        }
274    }
275}
276
277/// An IPv4 forwarding entry or an IPv6 forwarding entry.
278#[allow(missing_docs)]
279#[derive(Debug, Copy, Clone, Eq, GenericOverIp, PartialEq)]
280#[generic_over_ip()]
281pub enum EntryEither<D> {
282    V4(Entry<Ipv4Addr, D>),
283    V6(Entry<Ipv6Addr, D>),
284}
285
286impl<A: IpAddress, D> From<Entry<A, D>> for EntryEither<D> {
287    fn from(entry: Entry<A, D>) -> EntryEither<D> {
288        A::Version::map_ip(entry, EntryEither::V4, EntryEither::V6)
289    }
290}
291
292/// `OrderedLocality` provides an implementation of `core::cmp::PartialOrd` for
293/// a route's "locality".
294// Define an enum, so that `OnLink` routes are sorted before `OffLink` routes.
295// See https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#derivable for
296// more details.
297#[derive(PartialEq, PartialOrd, Eq, Ord)]
298pub(crate) enum OrderedLocality {
299    // The route does not have a gateway.
300    OnLink,
301    // The route does have a gateway.
302    OffLink,
303}
304
305// `OrderedRoute` provides an implementation of `core::cmp::PartialOrd`
306// for routes. Note that the fields are consulted in the order they are
307// declared. For more details, see
308// https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#derivable.
309#[derive(PartialEq, PartialOrd, Eq, Ord)]
310pub(crate) struct OrderedEntry<'a, A: IpAddress, D> {
311    // Order longer prefixes before shorter prefixes.
312    prefix_len: core::cmp::Reverse<u8>,
313    // Order `OnLink` routes before `OffLink` routes.
314    //
315    // Adhere to industry norms by considering the route locality before the
316    // route metric.
317    locality: OrderedLocality,
318    // Order lower metrics before larger metrics.
319    metric: u32,
320    // Earlier-added routes should come before later ones.
321    generation: Generation,
322    // To provide a consistent ordering, tiebreak using the remaining fields
323    // of the entry.
324    subnet_addr: A,
325    device: &'a D,
326    // Note that while this appears to duplicate the ordering provided by
327    // `locality`, it's important that we sort above on presence of the gateway
328    // and not on the actual address of the gateway. The latter is only used
329    // for tiebreaking at the end to provide a total order. Duplicating it this
330    // way allows us to avoid writing a manual `PartialOrd` impl.
331    gateway: Option<SpecifiedAddr<A>>,
332}
333
334impl<'a, A: IpAddress, D> From<&'a EntryAndGeneration<A, D>> for OrderedEntry<'a, A, D> {
335    fn from(entry: &'a EntryAndGeneration<A, D>) -> OrderedEntry<'a, A, D> {
336        let EntryAndGeneration { entry: Entry { subnet, device, gateway, metric }, generation } =
337            entry;
338        OrderedEntry {
339            prefix_len: core::cmp::Reverse(subnet.prefix()),
340            metric: metric.value().into(),
341            locality: gateway.map_or(OrderedLocality::OnLink, |_gateway| OrderedLocality::OffLink),
342            generation: *generation,
343            subnet_addr: subnet.network(),
344            device: &device,
345            gateway: *gateway,
346        }
347    }
348}
349
350/// The next hop for a [`Destination`].
351#[derive(Debug, Copy, Clone, PartialEq, Eq)]
352pub enum NextHop<A: IpAddress>
353where
354    A::Version: BroadcastIpExt,
355{
356    /// Indicates that the next-hop for a the packet is the remote since it is a
357    /// neighboring node (on-link).
358    RemoteAsNeighbor,
359    /// Indicates that the next-hop is a gateway/router since the remote is not
360    /// a neighboring node (off-link).
361    Gateway(SpecifiedAddr<A>),
362    /// Indicates that the packet should be broadcast rather than sent to a
363    /// specific neighbor.
364    Broadcast(<A::Version as BroadcastIpExt>::BroadcastMarker),
365}
366
367impl<A: IpAddress> NextHop<A>
368where
369    A::Version: BroadcastIpExt,
370{
371    /// True if the `NextHop` is `Broadcast`.
372    pub fn is_broadcast(self) -> bool {
373        match self {
374            Self::Broadcast(_) => true,
375            Self::RemoteAsNeighbor | Self::Gateway(_) => false,
376        }
377    }
378}
379
380impl<A: IpAddress, NewIp: BroadcastIpExt> GenericOverIp<NewIp> for NextHop<A>
381where
382    A::Version: BroadcastIpExt,
383{
384    type Type = NextHop<NewIp::Addr>;
385}
386
387/// An IP Address that witnesses properties needed to be routed.
388pub type RoutableIpAddr<A> = SocketIpAddr<A>;
389
390/// The resolved route to a destination IP address.
391#[derive(Debug, Copy, Clone, PartialEq, Eq, GenericOverIp)]
392#[generic_over_ip(I, Ip)]
393pub struct ResolvedRoute<I: BroadcastIpExt, D> {
394    /// The source address to use when forwarding packets towards the
395    /// destination.
396    pub src_addr: IpDeviceAddr<I::Addr>,
397    /// The device over which this destination can be reached.
398    pub device: D,
399    /// Present when `device` is loopback with the device that the destination
400    /// address is assigned to.
401    ///
402    /// NB: it's possible that `local_delivery_device` is itself loopback.
403    pub local_delivery_device: Option<D>,
404    /// The next hop via which this destination can be reached.
405    pub next_hop: NextHop<I::Addr>,
406    /// The route's internal forwarding semantics.
407    pub internal_forwarding: InternalForwarding<D>,
408}
409
410/// Internal forwarding semantics.
411///
412/// Internal forwarding allows the netstack to behave as a Weak Host when
413/// forwarding is enabled on a device.
414///
415/// In a sending context, internal forwarding allows sending a packet out of a
416/// device using a source address not assigned to that device, provided that the
417/// source address is assigned to another device, and that other device has
418/// forwarding enabled.
419///
420/// In a receiving context, internal forwarding allows receiving a packet that
421/// was destined to an address not assigned to the device that is arrived on,
422/// provided that destination address is assigned to another device and the
423/// device the packet arrived on has forwarding enabled.
424#[derive(Debug, Copy, Clone, PartialEq, Eq)]
425pub enum InternalForwarding<D> {
426    /// Internal forwarding is being used. The relevant address (src addr when
427    /// sending, dst addr when receiving) is assigned to the provided device.
428    Used(D),
429    /// Internal forwarding is not being used.
430    NotUsed,
431}
432
433impl<D> InternalForwarding<D> {
434    /// Applies the given callback to the held device identifier.
435    pub fn map_device<F: FnOnce(D) -> O, O>(self, cb: F) -> InternalForwarding<O> {
436        match self {
437            InternalForwarding::NotUsed => InternalForwarding::NotUsed,
438            InternalForwarding::Used(d) => InternalForwarding::Used(cb(d)),
439        }
440    }
441}
442
443/// The destination of an outbound IP packet.
444///
445/// Outbound IP packets are sent to a particular device (specified by the
446/// `device` field).
447#[derive(Debug, Copy, Clone, PartialEq, Eq)]
448pub struct Destination<A: IpAddress, D>
449where
450    A::Version: BroadcastIpExt,
451{
452    /// Indicates the next hop via which this destination can be reached.
453    pub next_hop: NextHop<A>,
454    /// Indicates the device over which this destination can be reached.
455    pub device: D,
456}