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::{IpExt, Marks};
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, Ipv6ExtensionHeaderData};
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
39/// Information for an incoming packet.
40///
41/// This is given to upper layers to provide extra information on locally
42/// delivered IP packets.
43#[derive(Debug, Clone)]
44pub struct LocalDeliveryPacketInfo<I: IpExt, H: IpHeaderInfo<I>> {
45    /// Packet metadata calculated by the stack.
46    pub meta: ReceiveIpPacketMeta<I>,
47    /// Accessor for extra information in IP header.
48    pub header_info: H,
49    /// The marks carried by the incoming packet.
50    pub marks: Marks,
51}
52
53/// Abstracts extracting information from IP headers for upper layers.
54///
55/// Implemented for the combination of fixed header and options of IPv4 and IPv6
56/// packets, so we can ensure this information is always extracted from packets,
57/// including when they're rewritten by filters.
58///
59/// This is a trait so:
60/// - We're gating here and acknowledging all the information necessary by upper
61///   layers.
62/// - A [fake implementation] can be provided without constructing a full
63///   IPv4/IPv6 packet.
64///
65/// [fake implementation]: testutil::FakeIpHeaderInfo
66pub trait IpHeaderInfo<I> {
67    /// DSCP and ECN values received in Traffic Class or TOS field.
68    fn dscp_and_ecn(&self) -> DscpAndEcn;
69
70    /// The TTL (IPv4) or Hop Limit (IPv6) of the received packet.
71    fn hop_limit(&self) -> u8;
72
73    /// Returns true if the router alert option (IPv4) or extension header
74    /// (IPv6) is present on the packet.
75    fn router_alert(&self) -> bool;
76
77    /// Returns the IP header as bytes.
78    fn as_bytes(&self) -> [&[u8]; 2];
79}
80
81pub(crate) struct Ipv4HeaderInfo<'a> {
82    pub(crate) prefix: &'a packet_formats::ipv4::HeaderPrefix,
83    pub(crate) options: packet_formats::ipv4::Options<&'a [u8]>,
84}
85
86impl IpHeaderInfo<Ipv4> for Ipv4HeaderInfo<'_> {
87    fn dscp_and_ecn(&self) -> DscpAndEcn {
88        self.prefix.dscp_and_ecn()
89    }
90
91    fn hop_limit(&self) -> u8 {
92        self.prefix.ttl()
93    }
94
95    fn router_alert(&self) -> bool {
96        self.options.iter().any(|opt| matches!(opt, Ipv4Option::RouterAlert { .. }))
97    }
98
99    fn as_bytes(&self) -> [&[u8]; 2] {
100        use zerocopy::IntoBytes as _;
101        let Self { prefix, options } = self;
102        [prefix.as_bytes(), options.bytes()]
103    }
104}
105
106pub(crate) struct Ipv6HeaderInfo<'a> {
107    pub(crate) fixed: &'a packet_formats::ipv6::FixedHeader,
108    pub(crate) extension: packet_formats::ipv6::ExtensionHeaders<'a>,
109}
110
111impl IpHeaderInfo<Ipv6> for Ipv6HeaderInfo<'_> {
112    fn dscp_and_ecn(&self) -> DscpAndEcn {
113        self.fixed.dscp_and_ecn()
114    }
115
116    fn hop_limit(&self) -> u8 {
117        self.fixed.hop_limit()
118    }
119
120    fn router_alert(&self) -> bool {
121        self.extension.iter().any(|h| match h.data() {
122            Ipv6ExtensionHeaderData::HopByHopOptions { options } => {
123                options.iter().any(|h| matches!(h.data, HopByHopOptionData::RouterAlert { .. }))
124            }
125            _ => false,
126        })
127    }
128
129    fn as_bytes(&self) -> [&[u8]; 2] {
130        use zerocopy::IntoBytes as _;
131        let Self { fixed, extension } = self;
132        [fixed.as_bytes(), extension.bytes()]
133    }
134}
135
136#[cfg(any(test, feature = "testutils"))]
137pub(crate) mod testutil {
138    use super::*;
139    use alloc::vec::Vec;
140
141    /// Handroll a default impl for `ReceiveIpPacketMeta` only for tests to
142    /// prevent accidental usage.
143    impl<I: IpExt> Default for ReceiveIpPacketMeta<I> {
144        fn default() -> Self {
145            Self { broadcast: None, transparent_override: None }
146        }
147    }
148
149    impl<I: IpExt> Default for LocalDeliveryPacketInfo<I, FakeIpHeaderInfo> {
150        fn default() -> Self {
151            Self {
152                meta: Default::default(),
153                header_info: Default::default(),
154                marks: Default::default(),
155            }
156        }
157    }
158
159    /// A fake implementation of [`IpHeaderInfo`].
160    #[derive(Debug, Default, Clone)]
161    pub struct FakeIpHeaderInfo {
162        /// The value returned by [`IpHeaderInfo::dscp_and_ecn`].
163        pub dscp_and_ecn: DscpAndEcn,
164        /// The value returned by [`IpHeaderInfo::hop_limit`].
165        pub hop_limit: u8,
166        /// The value returned by [`IpHeaderInfo::router_alert`].
167        pub router_alert: bool,
168        /// The value returned by [`IpHeaderInfo::as_bytes`].
169        pub as_bytes: [Vec<u8>; 2],
170    }
171
172    impl<I: IpExt> IpHeaderInfo<I> for FakeIpHeaderInfo {
173        fn dscp_and_ecn(&self) -> DscpAndEcn {
174            self.dscp_and_ecn
175        }
176
177        fn hop_limit(&self) -> u8 {
178            self.hop_limit
179        }
180
181        fn router_alert(&self) -> bool {
182            self.router_alert
183        }
184
185        fn as_bytes(&self) -> [&[u8]; 2] {
186            [&self.as_bytes[0], &self.as_bytes[1]]
187        }
188    }
189}