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.
45use core::convert::Infallible as Never;
6use core::fmt::Debug;
7use core::num::NonZeroU32;
89use 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};
1819/// `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.
23type BroadcastMarker: Debug + Copy + Clone + PartialEq + Eq + Send + Sync + 'static;
24}
2526impl BroadcastIpExt for Ipv4 {
27type BroadcastMarker = ();
28}
2930impl BroadcastIpExt for Ipv6 {
31type BroadcastMarker = Never;
32}
3334/// 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);
3940/// 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);
4546impl Mms {
47/// Creates a maximum packet size from an [`Mtu`] value.
48pub 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 }
5253/// Returns the maximum packet size.
54pub fn get(&self) -> NonZeroU32 {
55let Self(mms) = *self;
56 mms
57 }
58}
5960/// 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}
7273impl<I: IcmpIpExt> GenericOverIp<I> for Icmpv4ErrorCode {
74type Type = I::ErrorCode;
75}
7677/// 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}
8990impl<I: IcmpIpExt> GenericOverIp<I> for Icmpv6ErrorCode {
91type Type = I::ErrorCode;
92}
9394/// An ICMP error of either IPv4 or IPv6.
95#[derive(Debug, Clone, Copy)]
96pub enum IcmpErrorCode {
97/// ICMPv4 error.
98V4(Icmpv4ErrorCode),
99/// ICMPv6 error.
100V6(Icmpv6ErrorCode),
101}
102103impl From<Icmpv4ErrorCode> for IcmpErrorCode {
104fn from(v4_err: Icmpv4ErrorCode) -> Self {
105 IcmpErrorCode::V4(v4_err)
106 }
107}
108109impl From<Icmpv6ErrorCode> for IcmpErrorCode {
110fn from(v6_err: Icmpv6ErrorCode) -> Self {
111 IcmpErrorCode::V6(v6_err)
112 }
113}
114115/// 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`].
119type 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}
127128impl IcmpIpExt for Ipv4 {
129type ErrorCode = Icmpv4ErrorCode;
130}
131132impl IcmpIpExt for Ipv6 {
133type ErrorCode = Icmpv6ErrorCode;
134}
135136/// `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.
140type BroadcastMarker: Debug + Copy + Clone + PartialEq + Eq;
141}
142143impl IpTypesIpExt for Ipv4 {
144type BroadcastMarker = ();
145}
146147impl IpTypesIpExt for Ipv6 {
148type BroadcastMarker = Never;
149}
150151/// 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`].
157type RecvSrcAddr: Into<Self::Addr> + TryFrom<Self::Addr, Error: Debug> + Copy + Clone;
158/// The length of an IP header without any IP options.
159const IP_HEADER_LENGTH: NonZeroU32;
160/// The maximum payload size an IP payload can have.
161const IP_MAX_PAYLOAD_LENGTH: NonZeroU32;
162}
163164impl IpExt for Ipv4 {
165type RecvSrcAddr = Ipv4Addr;
166const IP_HEADER_LENGTH: NonZeroU32 =
167 NonZeroU32::new(packet_formats::ipv4::HDR_PREFIX_LEN as u32).unwrap();
168const IP_MAX_PAYLOAD_LENGTH: NonZeroU32 =
169 NonZeroU32::new(u16::MAX as u32 - Self::IP_HEADER_LENGTH.get()).unwrap();
170}
171172impl IpExt for Ipv6 {
173type RecvSrcAddr = Ipv6SourceAddr;
174const IP_HEADER_LENGTH: NonZeroU32 =
175 NonZeroU32::new(packet_formats::ipv6::IPV6_FIXED_HDR_LEN as u32).unwrap();
176const IP_MAX_PAYLOAD_LENGTH: NonZeroU32 = NonZeroU32::new(u16::MAX as u32).unwrap();
177}
178179/// 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>);
185186impl From<Option<u32>> for Mark {
187fn from(m: Option<u32>) -> Self {
188Self(m)
189 }
190}
191192/// Mark domains.
193#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumCount, EnumIter)]
194pub enum MarkDomain {
195/// The first mark.
196Mark1,
197/// The second mark.
198Mark2,
199}
200201const MARK_DOMAINS: usize = MarkDomain::COUNT;
202203/// 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]);
206207impl<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.
214pub fn new<U, IntoIter>(iter: IntoIter) -> Self
215where
216IntoIter: IntoIterator<Item = (MarkDomain, U)>,
217 T: From<Option<U>> + Copy,
218 {
219let mut storage = MarkStorage([None.into(); MARK_DOMAINS]);
220for (domain, value) in iter.into_iter() {
221*storage.get_mut(domain) = Some(value).into();
222 }
223 storage
224 }
225226fn domain_as_index(domain: MarkDomain) -> usize {
227match domain {
228 MarkDomain::Mark1 => 0,
229 MarkDomain::Mark2 => 1,
230 }
231 }
232233/// Gets an immutable reference to the mark at the given domain.
234pub fn get(&self, domain: MarkDomain) -> &T {
235let Self(inner) = self;
236&inner[Self::domain_as_index(domain)]
237 }
238239/// Gets a mutable reference to the mark at the given domain.
240pub fn get_mut(&mut self, domain: MarkDomain) -> &mut T {
241let Self(inner) = self;
242&mut inner[Self::domain_as_index(domain)]
243 }
244245/// Returns an iterator over the mark domains.
246pub fn iter(&self) -> impl Iterator<Item = (MarkDomain, &T)> {
247let Self(inner) = self;
248 MarkDomain::iter().map(move |domain| (domain, &inner[Self::domain_as_index(domain)]))
249 }
250251/// Zips with another storage so that the domains align.
252pub fn zip_with<'a, U>(
253&'a self,
254 MarkStorage(other): &'a MarkStorage<U>,
255 ) -> impl Iterator<Item = (MarkDomain, &'a T, &'a U)> + 'a {
256let Self(this) = self;
257 MarkDomain::iter().zip(this.iter().zip(other.iter())).map(|(d, (t, u))| (d, t, u))
258 }
259}
260261/// The 2 marks that can be attached to packets.
262pub type Marks = MarkStorage<Mark>;
263264impl Marks {
265/// Unmarked marks.
266pub const UNMARKED: Self = MarkStorage([Mark(None), Mark(None)]);
267}