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.
45//! Types and helpers used in local-delivery of packets.
67use core::num::NonZeroU16;
89use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6};
10use net_types::SpecifiedAddr;
11use netstack3_base::{IpExt, Marks};
12use packet_formats::ip::DscpAndEcn;
13use packet_formats::ipv4::options::Ipv4Option;
14use packet_formats::ipv4::Ipv4Header as _;
15use packet_formats::ipv6::ext_hdrs::{HopByHopOptionData, Ipv6ExtensionHeaderData};
16use packet_formats::ipv6::Ipv6Header as _;
1718/// 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.
23pub addr: SpecifiedAddr<I::Addr>,
24/// The local delivery port.
25pub port: NonZeroU16,
26}
2728/// 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.
33pub broadcast: Option<I::BroadcastMarker>,
3435/// Destination overrides for the transparent proxy.
36pub transparent_override: Option<TransparentLocalDelivery<I>>,
37}
3839/// 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.
46pub meta: ReceiveIpPacketMeta<I>,
47/// Accessor for extra information in IP header.
48pub header_info: H,
49/// The marks carried by the incoming packet.
50pub marks: Marks,
51}
5253/// 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.
68fn dscp_and_ecn(&self) -> DscpAndEcn;
6970/// The TTL (IPv4) or Hop Limit (IPv6) of the received packet.
71fn hop_limit(&self) -> u8;
7273/// Returns true if the router alert option (IPv4) or extension header
74 /// (IPv6) is present on the packet.
75fn router_alert(&self) -> bool;
76}
7778pub(crate) struct Ipv4HeaderInfo<'a> {
79pub(crate) prefix: &'a packet_formats::ipv4::HeaderPrefix,
80pub(crate) options: packet_formats::ipv4::Options<&'a [u8]>,
81}
8283impl IpHeaderInfo<Ipv4> for Ipv4HeaderInfo<'_> {
84fn dscp_and_ecn(&self) -> DscpAndEcn {
85self.prefix.dscp_and_ecn()
86 }
8788fn hop_limit(&self) -> u8 {
89self.prefix.ttl()
90 }
9192fn router_alert(&self) -> bool {
93self.options.iter().any(|opt| matches!(opt, Ipv4Option::RouterAlert { .. }))
94 }
95}
9697pub(crate) struct Ipv6HeaderInfo<'a> {
98pub(crate) fixed: &'a packet_formats::ipv6::FixedHeader,
99pub(crate) extension: packet_formats::ipv6::ExtensionHeaders<'a>,
100}
101102impl IpHeaderInfo<Ipv6> for Ipv6HeaderInfo<'_> {
103fn dscp_and_ecn(&self) -> DscpAndEcn {
104self.fixed.dscp_and_ecn()
105 }
106107fn hop_limit(&self) -> u8 {
108self.fixed.hop_limit()
109 }
110111fn router_alert(&self) -> bool {
112self.extension.iter().any(|h| match h.data() {
113 Ipv6ExtensionHeaderData::HopByHopOptions { options } => {
114 options.iter().any(|h| matches!(h.data, HopByHopOptionData::RouterAlert { .. }))
115 }
116_ => false,
117 })
118 }
119}
120121#[cfg(any(test, feature = "testutils"))]
122pub(crate) mod testutil {
123use super::*;
124125/// Handroll a default impl for `ReceiveIpPacketMeta` only for tests to
126 /// prevent accidental usage.
127impl<I: IpExt> Default for ReceiveIpPacketMeta<I> {
128fn default() -> Self {
129Self { broadcast: None, transparent_override: None }
130 }
131 }
132133impl<I: IpExt> Default for LocalDeliveryPacketInfo<I, FakeIpHeaderInfo> {
134fn default() -> Self {
135Self {
136 meta: Default::default(),
137 header_info: Default::default(),
138 marks: Default::default(),
139 }
140 }
141 }
142143/// A fake implementation of [`IpHeaderInfo`].
144#[derive(Debug, Default)]
145pub struct FakeIpHeaderInfo {
146/// The value returned by [`IpHeaderInfo::dscp_and_ecn`].
147pub dscp_and_ecn: DscpAndEcn,
148/// The value returned by [`IpHeaderInfo::hop_limit`].
149pub hop_limit: u8,
150/// The value returned by [`IpHeaderInfo::router_alert`].
151pub router_alert: bool,
152 }
153154impl<I: IpExt> IpHeaderInfo<I> for FakeIpHeaderInfo {
155fn dscp_and_ecn(&self) -> DscpAndEcn {
156self.dscp_and_ecn
157 }
158159fn hop_limit(&self) -> u8 {
160self.hop_limit
161 }
162163fn router_alert(&self) -> bool {
164self.router_alert
165 }
166 }
167}