netstack3_base/
ip.rs

1// Copyright 2024 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
5use core::convert::Infallible as Never;
6use core::fmt::Debug;
7use core::num::NonZeroU32;
8
9use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6SourceAddr, Mtu};
10use packet_formats::icmp::{
11    Icmpv4DestUnreachableCode, Icmpv4ParameterProblemCode, Icmpv4RedirectCode,
12    Icmpv4TimeExceededCode, Icmpv6DestUnreachableCode, Icmpv6ParameterProblemCode,
13    Icmpv6TimeExceededCode,
14};
15use packet_formats::ip::IpProtoExt;
16use strum::{EnumCount as _, IntoEnumIterator as _};
17use strum_macros::{EnumCount, EnumIter};
18
19/// `Ip` extension trait to assist in defining [`NextHop`].
20pub trait BroadcastIpExt: Ip {
21    /// A marker type carried by the [`NextHop::Broadcast`] variant to indicate
22    /// that it is uninhabited for IPv6.
23    type BroadcastMarker: Debug + Copy + Clone + PartialEq + Eq + Send + Sync + 'static;
24}
25
26impl BroadcastIpExt for Ipv4 {
27    type BroadcastMarker = ();
28}
29
30impl BroadcastIpExt for Ipv6 {
31    type BroadcastMarker = Never;
32}
33
34/// Wrapper struct to provide a convenient [`GenericOverIp`] impl for use
35/// with [`BroadcastIpExt::BroadcastMarker`].
36#[derive(GenericOverIp)]
37#[generic_over_ip(I, Ip)]
38pub struct WrapBroadcastMarker<I: BroadcastIpExt>(pub I::BroadcastMarker);
39
40/// Maximum packet size, that is the maximum IP payload one packet can carry.
41///
42/// More details from https://www.rfc-editor.org/rfc/rfc1122#section-3.3.2.
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub struct Mms(NonZeroU32);
45
46impl Mms {
47    /// Creates a maximum packet size from an [`Mtu`] value.
48    pub fn from_mtu<I: IpExt>(mtu: Mtu, options_size: u32) -> Option<Self> {
49        NonZeroU32::new(mtu.get().saturating_sub(I::IP_HEADER_LENGTH.get() + options_size))
50            .map(|mms| Self(mms.min(I::IP_MAX_PAYLOAD_LENGTH)))
51    }
52
53    /// Returns the maximum packet size.
54    pub fn get(&self) -> NonZeroU32 {
55        let Self(mms) = *self;
56        mms
57    }
58}
59
60/// An ICMPv4 error type and code.
61///
62/// Each enum variant corresponds to a particular error type, and contains the
63/// possible codes for that error type.
64#[derive(Copy, Clone, Debug, PartialEq)]
65#[allow(missing_docs)]
66pub enum Icmpv4ErrorCode {
67    DestUnreachable(Icmpv4DestUnreachableCode),
68    Redirect(Icmpv4RedirectCode),
69    TimeExceeded(Icmpv4TimeExceededCode),
70    ParameterProblem(Icmpv4ParameterProblemCode),
71}
72
73impl<I: IcmpIpExt> GenericOverIp<I> for Icmpv4ErrorCode {
74    type Type = I::ErrorCode;
75}
76
77/// An ICMPv6 error type and code.
78///
79/// Each enum variant corresponds to a particular error type, and contains the
80/// possible codes for that error type.
81#[derive(Copy, Clone, Debug, PartialEq)]
82#[allow(missing_docs)]
83pub enum Icmpv6ErrorCode {
84    DestUnreachable(Icmpv6DestUnreachableCode),
85    PacketTooBig,
86    TimeExceeded(Icmpv6TimeExceededCode),
87    ParameterProblem(Icmpv6ParameterProblemCode),
88}
89
90impl<I: IcmpIpExt> GenericOverIp<I> for Icmpv6ErrorCode {
91    type Type = I::ErrorCode;
92}
93
94/// An ICMP error of either IPv4 or IPv6.
95#[derive(Debug, Clone, Copy)]
96pub enum IcmpErrorCode {
97    /// ICMPv4 error.
98    V4(Icmpv4ErrorCode),
99    /// ICMPv6 error.
100    V6(Icmpv6ErrorCode),
101}
102
103impl From<Icmpv4ErrorCode> for IcmpErrorCode {
104    fn from(v4_err: Icmpv4ErrorCode) -> Self {
105        IcmpErrorCode::V4(v4_err)
106    }
107}
108
109impl From<Icmpv6ErrorCode> for IcmpErrorCode {
110    fn from(v6_err: Icmpv6ErrorCode) -> Self {
111        IcmpErrorCode::V6(v6_err)
112    }
113}
114
115/// An extension trait adding extra ICMP-related functionality to IP versions.
116pub trait IcmpIpExt: packet_formats::ip::IpExt + packet_formats::icmp::IcmpIpExt {
117    /// The type of error code for this version of ICMP - [`Icmpv4ErrorCode`] or
118    /// [`Icmpv6ErrorCode`].
119    type ErrorCode: Debug
120        + Copy
121        + PartialEq
122        + GenericOverIp<Self, Type = Self::ErrorCode>
123        + GenericOverIp<Ipv4, Type = Icmpv4ErrorCode>
124        + GenericOverIp<Ipv6, Type = Icmpv6ErrorCode>
125        + Into<IcmpErrorCode>;
126}
127
128impl IcmpIpExt for Ipv4 {
129    type ErrorCode = Icmpv4ErrorCode;
130}
131
132impl IcmpIpExt for Ipv6 {
133    type ErrorCode = Icmpv6ErrorCode;
134}
135
136/// `Ip` extension trait to assist in defining [`NextHop`].
137pub trait IpTypesIpExt: packet_formats::ip::IpExt {
138    /// A marker type carried by the [`NextHop::Broadcast`] variant to indicate
139    /// that it is uninhabited for IPv6.
140    type BroadcastMarker: Debug + Copy + Clone + PartialEq + Eq;
141}
142
143impl IpTypesIpExt for Ipv4 {
144    type BroadcastMarker = ();
145}
146
147impl IpTypesIpExt for Ipv6 {
148    type BroadcastMarker = Never;
149}
150
151/// An [`Ip`] extension trait adding functionality specific to the IP layer.
152pub trait IpExt: packet_formats::ip::IpExt + IcmpIpExt + BroadcastIpExt + IpProtoExt {
153    /// The type used to specify an IP packet's source address in a call to
154    /// [`IpTransportContext::receive_ip_packet`].
155    ///
156    /// For IPv4, this is `Ipv4Addr`. For IPv6, this is [`Ipv6SourceAddr`].
157    type RecvSrcAddr: Into<Self::Addr> + TryFrom<Self::Addr, Error: Debug> + Copy + Clone;
158    /// The length of an IP header without any IP options.
159    const IP_HEADER_LENGTH: NonZeroU32;
160    /// The maximum payload size an IP payload can have.
161    const IP_MAX_PAYLOAD_LENGTH: NonZeroU32;
162}
163
164impl IpExt for Ipv4 {
165    type RecvSrcAddr = Ipv4Addr;
166    const IP_HEADER_LENGTH: NonZeroU32 =
167        NonZeroU32::new(packet_formats::ipv4::HDR_PREFIX_LEN as u32).unwrap();
168    const IP_MAX_PAYLOAD_LENGTH: NonZeroU32 =
169        NonZeroU32::new(u16::MAX as u32 - Self::IP_HEADER_LENGTH.get()).unwrap();
170}
171
172impl IpExt for Ipv6 {
173    type RecvSrcAddr = Ipv6SourceAddr;
174    const IP_HEADER_LENGTH: NonZeroU32 =
175        NonZeroU32::new(packet_formats::ipv6::IPV6_FIXED_HDR_LEN as u32).unwrap();
176    const IP_MAX_PAYLOAD_LENGTH: NonZeroU32 = NonZeroU32::new(u16::MAX as u32).unwrap();
177}
178
179/// A mark attached to packets/sockets.
180///
181/// A mark can either be `None` or a `u32`. The mark can be carried by a
182/// socket or a packet, and `None` means the socket/packet is unmarked.
183#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
184pub struct Mark(pub Option<u32>);
185
186impl From<Option<u32>> for Mark {
187    fn from(m: Option<u32>) -> Self {
188        Self(m)
189    }
190}
191
192/// Mark domains.
193#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumCount, EnumIter)]
194pub enum MarkDomain {
195    /// The first mark.
196    Mark1,
197    /// The second mark.
198    Mark2,
199}
200
201const MARK_DOMAINS: usize = MarkDomain::COUNT;
202
203/// A storage backed by an array with the same cardinality of [`MarkDomain`].
204#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
205pub struct MarkStorage<T>([T; MARK_DOMAINS]);
206
207impl<T> MarkStorage<T> {
208    /// Creates [`MarkStorage`]s from an iterator of `(MarkDomain, U)`.
209    ///
210    /// An unspecified domain will remain default. For the same domain,
211    /// a later value in the iterator will override an earlier value.
212    ///
213    /// For a specified domain, `(Some(value)).into()` will be written.
214    pub fn new<U, IntoIter>(iter: IntoIter) -> Self
215    where
216        IntoIter: IntoIterator<Item = (MarkDomain, U)>,
217        T: From<Option<U>> + Copy,
218    {
219        let mut storage = MarkStorage([None.into(); MARK_DOMAINS]);
220        for (domain, value) in iter.into_iter() {
221            *storage.get_mut(domain) = Some(value).into();
222        }
223        storage
224    }
225
226    fn domain_as_index(domain: MarkDomain) -> usize {
227        match domain {
228            MarkDomain::Mark1 => 0,
229            MarkDomain::Mark2 => 1,
230        }
231    }
232
233    /// Gets an immutable reference to the mark at the given domain.
234    pub fn get(&self, domain: MarkDomain) -> &T {
235        let Self(inner) = self;
236        &inner[Self::domain_as_index(domain)]
237    }
238
239    /// Gets a mutable reference to the mark at the given domain.
240    pub fn get_mut(&mut self, domain: MarkDomain) -> &mut T {
241        let Self(inner) = self;
242        &mut inner[Self::domain_as_index(domain)]
243    }
244
245    /// Returns an iterator over the mark domains.
246    pub fn iter(&self) -> impl Iterator<Item = (MarkDomain, &T)> {
247        let Self(inner) = self;
248        MarkDomain::iter().map(move |domain| (domain, &inner[Self::domain_as_index(domain)]))
249    }
250
251    /// Zips with another storage so that the domains align.
252    pub fn zip_with<'a, U>(
253        &'a self,
254        MarkStorage(other): &'a MarkStorage<U>,
255    ) -> impl Iterator<Item = (MarkDomain, &'a T, &'a U)> + 'a {
256        let Self(this) = self;
257        MarkDomain::iter().zip(this.iter().zip(other.iter())).map(|(d, (t, u))| (d, t, u))
258    }
259}
260
261/// The 2 marks that can be attached to packets.
262pub type Marks = MarkStorage<Mark>;
263
264impl Marks {
265    /// Unmarked marks.
266    pub const UNMARKED: Self = MarkStorage([Mark(None), Mark(None)]);
267}