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