Skip to main content

netstack3_ip/
local_delivery.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
5//! Types and helpers used in local-delivery of packets.
6
7use core::num::NonZeroU16;
8
9use net_types::SpecifiedAddr;
10use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6};
11use netstack3_base::{GsoInfo, IpExt, Marks, NetworkParsingContext, PacketType};
12use packet_formats::ip::DscpAndEcn;
13use packet_formats::ipv4::Ipv4Header as _;
14use packet_formats::ipv4::options::Ipv4Option;
15use packet_formats::ipv6::Ipv6Header as _;
16use packet_formats::ipv6::ext_hdrs::{HopByHopOptionData, Ipv6ExtensionHeader};
17
18/// Informs the transport layer of parameters for transparent local delivery.
19#[derive(Debug, GenericOverIp, Clone)]
20#[generic_over_ip(I, Ip)]
21pub struct TransparentLocalDelivery<I: IpExt> {
22    /// The local delivery address.
23    pub addr: SpecifiedAddr<I::Addr>,
24    /// The local delivery port.
25    pub port: NonZeroU16,
26}
27
28/// Meta information for an incoming packet.
29#[derive(Debug, GenericOverIp, Clone)]
30#[generic_over_ip(I, Ip)]
31pub struct ReceiveIpPacketMeta<I: IpExt> {
32    /// Indicates that the packet was sent to a broadcast address.
33    pub broadcast: Option<I::BroadcastMarker>,
34
35    /// Destination overrides for the transparent proxy.
36    pub transparent_override: Option<TransparentLocalDelivery<I>>,
37
38    /// The parsing context for the received packet.
39    pub parsing_context: NetworkParsingContext,
40}
41
42/// Information for an incoming packet.
43///
44/// This is given to upper layers to provide extra information on locally
45/// delivered IP packets.
46#[derive(Debug, Clone)]
47pub struct LocalDeliveryPacketInfo<I: IpExt, H: IpHeaderInfo<I>> {
48    /// Packet metadata calculated by the stack.
49    pub meta: ReceiveIpPacketMeta<I>,
50    /// Accessor for extra information in IP header.
51    pub header_info: H,
52    /// The marks carried by the incoming packet.
53    pub marks: Marks,
54    /// The packet type.
55    pub packet_type: PacketType,
56    /// GSO metadata if the packet was coalesced from multiple segments by GRO.
57    pub gso_info: Option<GsoInfo>,
58}
59
60/// Abstracts extracting information from IP headers for upper layers.
61///
62/// Implemented for the combination of fixed header and options of IPv4 and IPv6
63/// packets, so we can ensure this information is always extracted from packets,
64/// including when they're rewritten by filters.
65///
66/// This is a trait so:
67/// - We're gating here and acknowledging all the information necessary by upper
68///   layers.
69/// - A [fake implementation] can be provided without constructing a full
70///   IPv4/IPv6 packet.
71///
72/// [fake implementation]: testutil::FakeIpHeaderInfo
73pub trait IpHeaderInfo<I> {
74    /// DSCP and ECN values received in Traffic Class or TOS field.
75    fn dscp_and_ecn(&self) -> DscpAndEcn;
76
77    /// The TTL (IPv4) or Hop Limit (IPv6) of the received packet.
78    fn hop_limit(&self) -> u8;
79
80    /// Returns true if the router alert option (IPv4) or extension header
81    /// (IPv6) is present on the packet.
82    fn router_alert(&self) -> bool;
83
84    /// Returns the IP header as bytes.
85    fn as_bytes(&self) -> [&[u8]; 2];
86}
87
88pub(crate) struct Ipv4HeaderInfo<'a> {
89    pub(crate) prefix: &'a packet_formats::ipv4::HeaderPrefix,
90    pub(crate) options: packet_formats::ipv4::Options<&'a [u8]>,
91}
92
93impl IpHeaderInfo<Ipv4> for Ipv4HeaderInfo<'_> {
94    fn dscp_and_ecn(&self) -> DscpAndEcn {
95        self.prefix.dscp_and_ecn()
96    }
97
98    fn hop_limit(&self) -> u8 {
99        self.prefix.ttl()
100    }
101
102    fn router_alert(&self) -> bool {
103        self.options.iter().any(|opt| matches!(opt, Ipv4Option::RouterAlert { .. }))
104    }
105
106    fn as_bytes(&self) -> [&[u8]; 2] {
107        use zerocopy::IntoBytes as _;
108        let Self { prefix, options } = self;
109        [prefix.as_bytes(), options.bytes()]
110    }
111}
112
113pub(crate) struct Ipv6HeaderInfo<'a> {
114    pub(crate) fixed: &'a packet_formats::ipv6::FixedHeader,
115    pub(crate) extension: packet_formats::ipv6::ExtensionHeaders<'a>,
116}
117
118impl IpHeaderInfo<Ipv6> for Ipv6HeaderInfo<'_> {
119    fn dscp_and_ecn(&self) -> DscpAndEcn {
120        self.fixed.dscp_and_ecn()
121    }
122
123    fn hop_limit(&self) -> u8 {
124        self.fixed.hop_limit()
125    }
126
127    fn router_alert(&self) -> bool {
128        self.extension.iter().any(|h| match h {
129            Ipv6ExtensionHeader::HopByHopOptions { options } => {
130                options.iter().any(|h| matches!(h.data, HopByHopOptionData::RouterAlert { .. }))
131            }
132            _ => false,
133        })
134    }
135
136    fn as_bytes(&self) -> [&[u8]; 2] {
137        use zerocopy::IntoBytes as _;
138        let Self { fixed, extension } = self;
139        [fixed.as_bytes(), extension.bytes()]
140    }
141}
142
143#[cfg(any(test, feature = "testutils"))]
144pub(crate) mod testutil {
145    use super::*;
146    use alloc::vec::Vec;
147
148    /// Handroll a default impl for `ReceiveIpPacketMeta` only for tests to
149    /// prevent accidental usage.
150    impl<I: IpExt> Default for ReceiveIpPacketMeta<I> {
151        fn default() -> Self {
152            Self {
153                broadcast: None,
154                transparent_override: None,
155                parsing_context: NetworkParsingContext::default(),
156            }
157        }
158    }
159
160    impl<I: IpExt> Default for LocalDeliveryPacketInfo<I, FakeIpHeaderInfo> {
161        fn default() -> Self {
162            Self {
163                meta: Default::default(),
164                header_info: Default::default(),
165                marks: Default::default(),
166                packet_type: PacketType::Host,
167                gso_info: None,
168            }
169        }
170    }
171
172    /// A fake implementation of [`IpHeaderInfo`].
173    #[derive(Debug, Default, Clone)]
174    pub struct FakeIpHeaderInfo {
175        /// The value returned by [`IpHeaderInfo::dscp_and_ecn`].
176        pub dscp_and_ecn: DscpAndEcn,
177        /// The value returned by [`IpHeaderInfo::hop_limit`].
178        pub hop_limit: u8,
179        /// The value returned by [`IpHeaderInfo::router_alert`].
180        pub router_alert: bool,
181        /// The value returned by [`IpHeaderInfo::as_bytes`].
182        pub as_bytes: [Vec<u8>; 2],
183    }
184
185    impl<I: IpExt> IpHeaderInfo<I> for FakeIpHeaderInfo {
186        fn dscp_and_ecn(&self) -> DscpAndEcn {
187            self.dscp_and_ecn
188        }
189
190        fn hop_limit(&self) -> u8 {
191            self.hop_limit
192        }
193
194        fn router_alert(&self) -> bool {
195            self.router_alert
196        }
197
198        fn as_bytes(&self) -> [&[u8]; 2] {
199            [&self.as_bytes[0], &self.as_bytes[1]]
200        }
201    }
202}