Skip to main content

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