Skip to main content

netstack3_ip/
base.rs

1// Copyright 2018 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
5use alloc::boxed::Box;
6use alloc::vec::Vec;
7use core::fmt::Debug;
8use core::hash::Hash;
9use core::marker::PhantomData;
10use core::num::NonZeroU8;
11use core::ops::ControlFlow;
12#[cfg(test)]
13use core::ops::DerefMut;
14use core::sync::atomic::{self, AtomicU16};
15
16use derivative::Derivative;
17use explicit::ResultExt as _;
18use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
19use log::{debug, trace};
20use net_types::ip::{
21    GenericOverIp, Ip, IpVersion, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Ipv6SourceAddr, Mtu, Subnet,
22};
23use net_types::{
24    LinkLocalAddress, MulticastAddr, MulticastAddress, NonMappedAddr, NonMulticastAddr,
25    SpecifiedAddr, SpecifiedAddress as _, Witness,
26};
27use netstack3_base::socket::{EitherStack, SocketIpAddr, SocketIpAddrExt as _};
28use netstack3_base::sync::{Mutex, PrimaryRc, RwLock, StrongRc, WeakRc};
29use netstack3_base::{
30    AnyDevice, BroadcastIpExt, CoreTimerContext, Counter, CounterCollectionSpec, CounterContext,
31    DeviceIdContext, DeviceIdentifier as _, ErrorAndSerializer, EventContext, GsoInfo,
32    HandleableTimer, InstantContext, InterfaceProperties, IpAddressId, IpDeviceAddr,
33    IpDeviceAddressIdContext, IpExt, LocalFrameDestination, MarkDomain, Marks, Matcher as _,
34    MatcherBindingsTypes, NestedIntoCoreTimerCtx, NetworkParsingContext,
35    NetworkSerializationContext, NotFoundError, ResourceCounterContext, RngContext,
36    SendFrameErrorReason, StrongDeviceIdentifier, TimerBindingsTypes, TimerContext, TimerHandler,
37    TxMetadata as _, TxMetadataBindingsTypes, WeakIpAddressId, WrapBroadcastMarker,
38};
39use netstack3_filter::{
40    self as filter, ConnectionDirection, ConntrackConnection, FilterBindingsContext,
41    FilterBindingsTypes, FilterHandler as _, FilterIpContext, FilterIpExt, FilterIpMetadata,
42    FilterIpPacket, FilterPacketMetadata, FilterTimerId, ForwardedPacket, IpPacket, MarkAction,
43    MaybeTransportPacket as _, RejectType, SocketInfo, TransportPacketSerializer, Tuple,
44    WeakConnectionError, WeakConntrackConnection,
45};
46use netstack3_hashmap::HashMap;
47use packet::{
48    Buf, BufferMut, GrowBuffer, LayoutBufferAlloc, NestablePacketBuilder as _, PacketConstraints,
49    ParsablePacket as _, ParseBuffer, ParseBufferMut, ParseMetadata, SerializeError,
50    Serializer as _,
51};
52use packet_formats::error::{Ipv6ParseError, ParseError};
53use packet_formats::ip::{DscpAndEcn, IpPacket as _, IpPacketBuilder as _};
54use packet_formats::ipv4::{Ipv4FragmentType, Ipv4Packet};
55use packet_formats::ipv6::{Ipv6Packet, Ipv6PacketRaw};
56use thiserror::Error;
57use zerocopy::SplitByteSlice;
58
59use crate::internal::counters::{IpCounters, IpCountersIpExt};
60use crate::internal::device::opaque_iid::IidSecret;
61use crate::internal::device::slaac::SlaacCounters;
62use crate::internal::device::state::{
63    IpAddressData, IpAddressFlags, IpDeviceStateBindingsTypes, IpDeviceStateIpExt, WeakAddressId,
64};
65use crate::internal::device::{
66    self, IpDeviceAddressContext, IpDeviceBindingsContext, IpDeviceIpExt, IpDeviceSendContext,
67};
68use crate::internal::fragmentation::{FragmentableIpSerializer, FragmentationIpExt, IpFragmenter};
69use crate::internal::gmp::GmpQueryHandler;
70use crate::internal::gmp::igmp::IgmpCounters;
71use crate::internal::gmp::mld::MldCounters;
72use crate::internal::icmp::counters::IcmpCountersIpExt;
73use crate::internal::icmp::{
74    IcmpBindingsTypes, IcmpError, IcmpErrorHandler, IcmpHandlerIpExt, Icmpv4Error, Icmpv4State,
75    Icmpv4StateBuilder, Icmpv6Error, Icmpv6State, Icmpv6StateBuilder,
76};
77use crate::internal::ipv6::Ipv6PacketAction;
78use crate::internal::local_delivery::{
79    IpHeaderInfo, Ipv4HeaderInfo, Ipv6HeaderInfo, LocalDeliveryPacketInfo, ReceiveIpPacketMeta,
80    TransparentLocalDelivery,
81};
82use crate::internal::multicast_forwarding::counters::MulticastForwardingCounters;
83use crate::internal::multicast_forwarding::route::{
84    MulticastRouteIpExt, MulticastRouteTarget, MulticastRouteTargets,
85};
86use crate::internal::multicast_forwarding::state::{
87    MulticastForwardingState, MulticastForwardingStateContext,
88};
89use crate::internal::multicast_forwarding::{
90    MulticastForwardingBindingsTypes, MulticastForwardingDeviceContext, MulticastForwardingEvent,
91    MulticastForwardingTimerId,
92};
93use crate::internal::path_mtu::{PmtuBindingsTypes, PmtuCache, PmtuTimerId};
94use crate::internal::raw::counters::RawIpSocketCounters;
95use crate::internal::raw::{RawIpSocketHandler, RawIpSocketMap, RawIpSocketsBindingsTypes};
96use crate::internal::reassembly::{
97    FragmentBindingsTypes, FragmentHandler, FragmentProcessingState, FragmentTimerId,
98    FragmentablePacket, IpPacketFragmentCache, ReassemblyIpExt,
99};
100use crate::internal::routing::rules::{Rule, RuleAction, RuleInput, RulesTable};
101use crate::internal::routing::{
102    IpRoutingBindingsTypes, IpRoutingDeviceContext, NonLocalSrcAddrPolicy, PacketOrigin,
103    RoutingTable,
104};
105use crate::internal::socket::{IpSocketBindingsContext, IpSocketContext, IpSocketHandler};
106use crate::internal::types::{
107    self, Destination, InternalForwarding, NextHop, ResolvedRoute, RoutableIpAddr,
108};
109use crate::internal::{ipv6, multicast_forwarding};
110
111#[cfg(test)]
112mod tests;
113
114/// Default IPv4 TTL.
115pub const DEFAULT_TTL: NonZeroU8 = NonZeroU8::new(64).unwrap();
116
117/// Hop limits for packets sent to multicast and unicast destinations.
118#[derive(Copy, Clone, Debug, Eq, PartialEq)]
119#[allow(missing_docs)]
120pub struct HopLimits {
121    pub unicast: NonZeroU8,
122    pub multicast: NonZeroU8,
123}
124
125/// Default hop limits for sockets.
126pub const DEFAULT_HOP_LIMITS: HopLimits =
127    HopLimits { unicast: DEFAULT_TTL, multicast: NonZeroU8::new(1).unwrap() };
128
129/// The IPv6 subnet that contains all addresses; `::/0`.
130// Safe because 0 is less than the number of IPv6 address bits.
131pub const IPV6_DEFAULT_SUBNET: Subnet<Ipv6Addr> =
132    unsafe { Subnet::new_unchecked(Ipv6::UNSPECIFIED_ADDRESS, 0) };
133
134/// Sidecar metadata passed along with the packet.
135///
136/// Note: This metadata may be regenerated when packet handling requires
137/// performing multiple actions (e.g. sending the packet out multiple interfaces
138/// as part of multicast forwarding).
139#[derive(Derivative)]
140#[derivative(Default(bound = ""))]
141pub struct IpLayerPacketMetadata<
142    I: packet_formats::ip::IpExt,
143    A,
144    BT: FilterBindingsTypes + TxMetadataBindingsTypes,
145> {
146    conntrack_connection_and_direction:
147        Option<(ConntrackConnection<I, A, BT>, ConnectionDirection)>,
148
149    /// Tx metadata associated with this packet.
150    ///
151    /// This may be non-default even in the rx path for looped back packets that
152    /// are still forcing tx frame ownership for sockets.
153    tx_metadata: BT::TxMetadata,
154
155    /// Marks attached to the packet that can be acted upon by routing/filtering.
156    marks: Marks,
157
158    /// Socket info of the associate socket if any.
159    socket_info: Option<SocketInfo>,
160
161    /// GSO metadata if the frame requires transport-layer segmentation (e.g.
162    /// because it was coalesced from multiple segments by GRO, or generated
163    /// locally as a large segment).
164    ///
165    /// Note: This is only for transport-layer segmentation, not IP-layer
166    /// fragmentation of reassembled packets.
167    gso_info: Option<GsoInfo>,
168
169    #[cfg(debug_assertions)]
170    drop_check: IpLayerPacketMetadataDropCheck,
171}
172
173/// A type that asserts, on drop, that it was intentionally being dropped.
174///
175/// NOTE: Unfortunately, debugging this requires backtraces, since track_caller
176/// won't do what we want (https://github.com/rust-lang/rust/issues/116942).
177/// Since this is only enabled in debug, the assumption is that stacktraces are
178/// enabled.
179#[cfg(debug_assertions)]
180#[derive(Default)]
181struct IpLayerPacketMetadataDropCheck {
182    okay_to_drop: bool,
183}
184
185/// Metadata that is produced and consumed by the IP layer for each packet, but
186/// which also traverses the device layer.
187#[derive(Derivative)]
188#[derivative(Debug(bound = ""), Default(bound = ""))]
189pub struct DeviceIpLayerMetadata<BT: TxMetadataBindingsTypes> {
190    /// Weak reference to this packet's connection tracking entry, if the packet is
191    /// tracked.
192    ///
193    /// This allows NAT to consistently associate locally-generated, looped-back
194    /// packets with the same connection at every filtering hook even when NAT may
195    /// have been performed on them, causing them to no longer match the original or
196    /// reply tuples of the connection.
197    conntrack_entry: Option<(WeakConntrackConnection, ConnectionDirection)>,
198    /// Tx metadata associated with this packet.
199    ///
200    /// This may be non-default even in the rx path for looped back packets that
201    /// are still forcing tx frame ownership for sockets.
202    tx_metadata: BT::TxMetadata,
203    /// Marks attached to this packet. For all the incoming packets, they are None
204    /// by default but can be changed by a filtering rule.
205    ///
206    /// Note: The marks will be preserved if the packet is being looped back, i.e.,
207    /// the receiver will be able to observe the marks set by the sender. This is
208    /// consistent with Linux behavior.
209    marks: Marks,
210}
211
212impl<BT: TxMetadataBindingsTypes> DeviceIpLayerMetadata<BT> {
213    /// Discards the remaining IP layer information and returns only the tx
214    /// metadata used for buffer ownership.
215    pub fn into_tx_metadata(self) -> BT::TxMetadata {
216        self.tx_metadata
217    }
218    /// Creates new IP layer metadata with the marks.
219    #[cfg(any(test, feature = "testutils"))]
220    pub fn with_marks(marks: Marks) -> Self {
221        Self { conntrack_entry: None, tx_metadata: Default::default(), marks }
222    }
223}
224
225impl<
226    I: IpLayerIpExt,
227    A: WeakIpAddressId<I::Addr>,
228    BT: FilterBindingsTypes + TxMetadataBindingsTypes,
229> IpLayerPacketMetadata<I, A, BT>
230{
231    fn from_device_ip_layer_metadata<CC, D>(
232        core_ctx: &mut CC,
233        device: &D,
234        DeviceIpLayerMetadata { conntrack_entry, tx_metadata, marks }: DeviceIpLayerMetadata<BT>,
235        gso_info: Option<GsoInfo>,
236    ) -> Self
237    where
238        CC: ResourceCounterContext<D, IpCounters<I>>,
239    {
240        let conntrack_connection_and_direction = match conntrack_entry
241            .map(|(conn, dir)| conn.into_inner().map(|conn| (conn, dir)))
242            .transpose()
243        {
244            // Either the packet was tracked and we've preserved its conntrack entry across
245            // loopback, or it was untracked and we just stash the `None`.
246            Ok(conn_and_dir) => conn_and_dir,
247            // Conntrack entry was removed from table after packet was enqueued in loopback.
248            Err(WeakConnectionError::EntryRemoved) => None,
249            // Conntrack entry no longer matches the packet (for example, it could be that
250            // this is an IPv6 packet that was modified at the device layer and therefore it
251            // no longer matches its IPv4 conntrack entry).
252            Err(WeakConnectionError::InvalidEntry) => {
253                core_ctx.increment_both(device, |c| &c.invalid_cached_conntrack_entry);
254                None
255            }
256        };
257
258        Self {
259            conntrack_connection_and_direction,
260            tx_metadata,
261            marks,
262            // `tx_metadata` belongs to the sending socket (preserved across
263            // loopback for TX buffer accounting). On ingress, `socket_info` must
264            // only reflect the receiving socket (populated later by early demux).
265            socket_info: None,
266            gso_info,
267            #[cfg(debug_assertions)]
268            drop_check: Default::default(),
269        }
270    }
271}
272
273/// The result of splitting metadata for multicast replication via
274/// [`IpLayerPacketMetadata::split_for_multicast`].
275pub(crate) struct SplitMulticastPacketMetadata<I, A, BT>
276where
277    I: packet_formats::ip::IpExt,
278    BT: FilterBindingsTypes + TxMetadataBindingsTypes,
279{
280    pub(crate) primary: IpLayerPacketMetadata<I, A, BT>,
281    pub(crate) secondary: IpLayerPacketMetadata<I, A, BT>,
282}
283
284impl<I: IpExt, A, BT: FilterBindingsTypes + TxMetadataBindingsTypes>
285    IpLayerPacketMetadata<I, A, BT>
286{
287    /// Splits metadata for multicast replication into [`SplitMulticastPacketMetadata`].
288    ///
289    /// The `primary` instance retains unique resources
290    /// (`conntrack_connection_and_direction` and `tx_metadata`), while the
291    /// `secondary` instance receives a copy of shareable metadata (`marks`,
292    /// `socket_info`, and `gso_info`) with default unique resources for
293    /// subsequent replications.
294    pub(crate) fn split_for_multicast(self) -> SplitMulticastPacketMetadata<I, A, BT> {
295        let secondary = Self {
296            conntrack_connection_and_direction: None,
297            tx_metadata: Default::default(),
298            marks: self.marks,
299            socket_info: self.socket_info.clone(),
300            gso_info: self.gso_info,
301            #[cfg(debug_assertions)]
302            drop_check: Default::default(),
303        };
304        SplitMulticastPacketMetadata { primary: self, secondary }
305    }
306
307    pub(crate) fn new_local_tx(
308        tx_metadata: BT::TxMetadata,
309        marks: Marks,
310        gso_info: Option<GsoInfo>,
311    ) -> Self {
312        let socket_info = tx_metadata.socket_info();
313        Self {
314            conntrack_connection_and_direction: None,
315            tx_metadata,
316            marks,
317            socket_info,
318            gso_info,
319            #[cfg(debug_assertions)]
320            drop_check: Default::default(),
321        }
322    }
323
324    pub(crate) fn into_parts(
325        self,
326    ) -> (
327        Option<(ConntrackConnection<I, A, BT>, ConnectionDirection)>,
328        BT::TxMetadata,
329        Marks,
330        Option<SocketInfo>,
331        Option<GsoInfo>,
332    ) {
333        let Self {
334            tx_metadata,
335            marks,
336            conntrack_connection_and_direction,
337            socket_info,
338            gso_info,
339            #[cfg(debug_assertions)]
340            mut drop_check,
341        } = self;
342        #[cfg(debug_assertions)]
343        {
344            drop_check.okay_to_drop = true;
345        }
346        (conntrack_connection_and_direction, tx_metadata, marks, socket_info, gso_info)
347    }
348
349    /// Acknowledge that it's okay to drop this packet metadata.
350    ///
351    /// When compiled with debug assertions, dropping [`IplayerPacketMetadata`]
352    /// will panic if this method has not previously been called.
353    pub(crate) fn acknowledge_drop(self) {
354        #[cfg(debug_assertions)]
355        {
356            let mut this = self;
357            this.drop_check.okay_to_drop = true;
358        }
359    }
360
361    /// Returns the tx metadata associated with this packet.
362    pub(crate) fn tx_metadata(&self) -> &BT::TxMetadata {
363        &self.tx_metadata
364    }
365
366    /// Returns the marks attached to this packet.
367    pub(crate) fn marks(&self) -> &Marks {
368        &self.marks
369    }
370}
371
372#[cfg(debug_assertions)]
373impl Drop for IpLayerPacketMetadataDropCheck {
374    fn drop(&mut self) {
375        if !self.okay_to_drop {
376            panic!(
377                "IpLayerPacketMetadata dropped without acknowledgement.  https://fxbug.dev/334127474"
378            );
379        }
380    }
381}
382
383impl<I: packet_formats::ip::IpExt, A, BT: FilterBindingsTypes + TxMetadataBindingsTypes>
384    FilterIpMetadata<I, A, BT> for IpLayerPacketMetadata<I, A, BT>
385{
386    fn take_connection_and_direction(
387        &mut self,
388    ) -> Option<(ConntrackConnection<I, A, BT>, ConnectionDirection)> {
389        self.conntrack_connection_and_direction.take()
390    }
391
392    fn replace_connection_and_direction(
393        &mut self,
394        conn: ConntrackConnection<I, A, BT>,
395        direction: ConnectionDirection,
396    ) -> Option<ConntrackConnection<I, A, BT>> {
397        self.conntrack_connection_and_direction.replace((conn, direction)).map(|(conn, _dir)| conn)
398    }
399}
400
401impl<I: packet_formats::ip::IpExt, A, BT: FilterBindingsTypes + TxMetadataBindingsTypes>
402    FilterPacketMetadata for IpLayerPacketMetadata<I, A, BT>
403{
404    fn apply_mark_action(&mut self, domain: MarkDomain, action: MarkAction) {
405        action.apply(self.marks.get_mut(domain))
406    }
407
408    fn socket_info(&self) -> Option<SocketInfo> {
409        self.socket_info.clone()
410    }
411
412    fn marks(&self) -> &Marks {
413        &self.marks
414    }
415}
416
417/// Send errors observed at or above the IP layer that carry a serializer.
418pub type IpSendFrameError<S> = ErrorAndSerializer<IpSendFrameErrorReason, S>;
419
420/// Send error cause for [`IpSendFrameError`].
421#[derive(Debug, PartialEq)]
422pub enum IpSendFrameErrorReason {
423    /// Error comes from the device layer.
424    Device(SendFrameErrorReason),
425    /// The frame's source or destination address is in the loopback subnet, but
426    /// the target device is not the loopback device.
427    IllegalLoopbackAddress,
428}
429
430impl From<SendFrameErrorReason> for IpSendFrameErrorReason {
431    fn from(value: SendFrameErrorReason) -> Self {
432        Self::Device(value)
433    }
434}
435
436/// The execution context provided by a transport layer protocol to the IP
437/// layer.
438///
439/// An implementation for `()` is provided which indicates that a particular
440/// transport layer protocol is unsupported.
441pub trait IpTransportContext<I, BC, CC>
442where
443    I: IpLayerIpExt,
444    CC: DeviceIdContext<AnyDevice> + ?Sized,
445{
446    /// Type used to identify sockets for early demux.
447    type EarlyDemuxSocket;
448
449    /// Performs early demux.
450    ///
451    /// Tries to match the packet with a connected socket that will receive the
452    /// packet. If a match is found, the socket information is passed to
453    /// `LOCAL_INGRESS` filters. The socket is also passed to
454    /// `receive_ip_packet` to avoid demuxing the packet twice.
455    ///
456    /// The socket may be invalidated if the source address is changed by SNAT.
457    /// In that case, `receive_ip_packet` is called with `early_demux_socket`
458    /// set to `None`.
459    fn early_demux<B: ParseBuffer>(
460        core_ctx: &mut CC,
461        device: &CC::DeviceId,
462        src_ip: I::Addr,
463        dst_ip: I::Addr,
464        buffer: B,
465    ) -> Option<Self::EarlyDemuxSocket>;
466
467    /// Receive an ICMP error message.
468    ///
469    /// All arguments beginning with `original_` are fields from the IP packet
470    /// that triggered the error. The `original_body` is provided here so that
471    /// the error can be associated with a transport-layer socket. `device`
472    /// identifies the device that received the ICMP error message packet.
473    ///
474    /// While ICMPv4 error messages are supposed to contain the first 8 bytes of
475    /// the body of the offending packet, and ICMPv6 error messages are supposed
476    /// to contain as much of the offending packet as possible without violating
477    /// the IPv6 minimum MTU, the caller does NOT guarantee that either of these
478    /// hold. It is `receive_icmp_error`'s responsibility to handle any length
479    /// of `original_body`, and to perform any necessary validation.
480    fn receive_icmp_error(
481        core_ctx: &mut CC,
482        bindings_ctx: &mut BC,
483        device: &CC::DeviceId,
484        original_src_ip: Option<SpecifiedAddr<I::Addr>>,
485        original_dst_ip: SpecifiedAddr<I::Addr>,
486        original_body: &[u8],
487        err: I::ErrorCode,
488    );
489
490    /// Receive a transport layer packet in an IP packet.
491    ///
492    /// In the event of an unreachable port, `receive_ip_packet` returns the
493    /// buffer in its original state (with the transport packet un-parsed) in
494    /// the `Err` variant.
495    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
496        core_ctx: &mut CC,
497        bindings_ctx: &mut BC,
498        device: &CC::DeviceId,
499        src_ip: I::RecvSrcAddr,
500        dst_ip: SpecifiedAddr<I::Addr>,
501        buffer: B,
502        info: &mut LocalDeliveryPacketInfo<I, H>,
503        early_demux_socket: Option<Self::EarlyDemuxSocket>,
504    ) -> Result<(), (B, I::IcmpError)>;
505}
506
507/// The base execution context provided by the IP layer to transport layer
508/// protocols.
509pub trait BaseTransportIpContext<I: IpExt, BC>: DeviceIdContext<AnyDevice> {
510    /// The iterator given to
511    /// [`BaseTransportIpContext::with_devices_with_assigned_addr`].
512    type DevicesWithAddrIter<'s>: Iterator<Item = Self::DeviceId>;
513
514    /// Is this one of our local addresses, and is it in the assigned state?
515    ///
516    /// Calls `cb` with an iterator over all the local interfaces for which
517    /// `addr` is an associated address, and, for IPv6, for which it is in the
518    /// "assigned" state.
519    fn with_devices_with_assigned_addr<O, F: FnOnce(Self::DevicesWithAddrIter<'_>) -> O>(
520        &mut self,
521        addr: SpecifiedAddr<I::Addr>,
522        cb: F,
523    ) -> O;
524
525    /// Get default hop limits.
526    ///
527    /// If `device` is not `None` and exists, its hop limits will be returned.
528    /// Otherwise the system defaults are returned.
529    fn get_default_hop_limits(&mut self, device: Option<&Self::DeviceId>) -> HopLimits;
530
531    /// Gets the original destination for the tracked connection indexed by
532    /// `tuple`, which includes the source and destination addresses and
533    /// transport-layer ports as well as the transport protocol number.
534    fn get_original_destination(&mut self, tuple: &Tuple<I>) -> Option<(I::Addr, u16)>;
535}
536
537/// A marker trait for the traits required by the transport layer from the IP
538/// layer.
539pub trait TransportIpContext<I: IpExt + FilterIpExt, BC: TxMetadataBindingsTypes>:
540    BaseTransportIpContext<I, BC> + IpSocketHandler<I, BC>
541{
542}
543
544impl<I, CC, BC> TransportIpContext<I, BC> for CC
545where
546    I: IpExt + FilterIpExt,
547    CC: BaseTransportIpContext<I, BC> + IpSocketHandler<I, BC>,
548    BC: TxMetadataBindingsTypes,
549{
550}
551
552/// Abstraction over the ability to join and leave multicast groups.
553pub trait MulticastMembershipHandler<I: Ip, BC>: DeviceIdContext<AnyDevice> {
554    /// Requests that the specified device join the given multicast group.
555    ///
556    /// If this method is called multiple times with the same device and
557    /// address, the device will remain joined to the multicast group until
558    /// [`MulticastTransportIpContext::leave_multicast_group`] has been called
559    /// the same number of times.
560    fn join_multicast_group(
561        &mut self,
562        bindings_ctx: &mut BC,
563        device: &Self::DeviceId,
564        addr: MulticastAddr<I::Addr>,
565    );
566
567    /// Requests that the specified device leave the given multicast group.
568    ///
569    /// Each call to this method must correspond to an earlier call to
570    /// [`MulticastTransportIpContext::join_multicast_group`]. The device
571    /// remains a member of the multicast group so long as some call to
572    /// `join_multicast_group` has been made without a corresponding call to
573    /// `leave_multicast_group`.
574    fn leave_multicast_group(
575        &mut self,
576        bindings_ctx: &mut BC,
577        device: &Self::DeviceId,
578        addr: MulticastAddr<I::Addr>,
579    );
580
581    /// Selects a default device with which to join the given multicast group.
582    ///
583    /// The selection is made by consulting the routing table; If there is no
584    /// route available to the given address, an error is returned.
585    fn select_device_for_multicast_group(
586        &mut self,
587        addr: MulticastAddr<I::Addr>,
588        marks: &Marks,
589    ) -> Result<Self::DeviceId, ResolveRouteError>;
590}
591
592// TODO(joshlf): With all 256 protocol numbers (minus reserved ones) given their
593// own associated type in both traits, running `cargo check` on a 2018 MacBook
594// Pro takes over a minute. Eventually - and before we formally publish this as
595// a library - we should identify the bottleneck in the compiler and optimize
596// it. For the time being, however, we only support protocol numbers that we
597// actually use (TCP and UDP).
598
599/// Enables a blanket implementation of [`TransportIpContext`].
600///
601/// Implementing this marker trait for a type enables a blanket implementation
602/// of `TransportIpContext` given the other requirements are met.
603pub trait UseTransportIpContextBlanket {}
604
605/// An iterator supporting the blanket implementation of
606/// [`BaseTransportIpContext::with_devices_with_assigned_addr`].
607pub struct AssignedAddressDeviceIterator<Iter, I, D>(Iter, PhantomData<(I, D)>);
608
609impl<Iter, I, D> Iterator for AssignedAddressDeviceIterator<Iter, I, D>
610where
611    Iter: Iterator<Item = (D, I::AddressStatus)>,
612    I: IpLayerIpExt,
613{
614    type Item = D;
615    fn next(&mut self) -> Option<D> {
616        let Self(iter, PhantomData) = self;
617        iter.by_ref().find_map(|(device, state)| is_unicast_assigned::<I>(&state).then_some(device))
618    }
619}
620
621impl<
622    I: IpLayerIpExt,
623    BC: FilterBindingsContext<CC::DeviceId> + TxMetadataBindingsTypes + IpRoutingBindingsTypes,
624    CC: IpDeviceContext<I>
625        + IpSocketHandler<I, BC>
626        + IpStateContext<I, BC>
627        + FilterIpContext<I, BC>
628        + UseTransportIpContextBlanket,
629> BaseTransportIpContext<I, BC> for CC
630{
631    type DevicesWithAddrIter<'s> =
632        AssignedAddressDeviceIterator<CC::DeviceAndAddressStatusIter<'s>, I, CC::DeviceId>;
633
634    fn with_devices_with_assigned_addr<O, F: FnOnce(Self::DevicesWithAddrIter<'_>) -> O>(
635        &mut self,
636        addr: SpecifiedAddr<I::Addr>,
637        cb: F,
638    ) -> O {
639        self.with_address_statuses(addr, |it| cb(AssignedAddressDeviceIterator(it, PhantomData)))
640    }
641
642    fn get_default_hop_limits(&mut self, device: Option<&Self::DeviceId>) -> HopLimits {
643        match device {
644            Some(device) => HopLimits {
645                unicast: IpDeviceEgressStateContext::<I>::get_hop_limit(self, device),
646                ..DEFAULT_HOP_LIMITS
647            },
648            None => DEFAULT_HOP_LIMITS,
649        }
650    }
651
652    fn get_original_destination(&mut self, tuple: &Tuple<I>) -> Option<(I::Addr, u16)> {
653        self.with_filter_state(|state| {
654            let conn = state.conntrack.get_connection(&tuple)?;
655
656            if !conn.destination_nat() {
657                return None;
658            }
659
660            // The tuple marking the original direction of the connection is
661            // never modified by NAT. This means it can be used to recover the
662            // destination before NAT was performed.
663            let original = conn.original_tuple();
664            Some((original.dst_addr, original.dst_port_or_id))
665        })
666    }
667}
668
669/// The status of an IP address on an interface.
670#[derive(Debug, PartialEq)]
671#[allow(missing_docs)]
672pub enum AddressStatus<S> {
673    Present(S),
674    Unassigned,
675}
676
677impl<S> AddressStatus<S> {
678    fn into_present(self) -> Option<S> {
679        match self {
680            Self::Present(s) => Some(s),
681            Self::Unassigned => None,
682        }
683    }
684}
685
686impl AddressStatus<Ipv4PresentAddressStatus> {
687    /// Creates an IPv4 `AddressStatus` for `addr` on `device`.
688    pub fn from_context_addr_v4<
689        BC: IpDeviceStateBindingsTypes,
690        CC: device::IpDeviceStateContext<Ipv4, BC> + GmpQueryHandler<Ipv4, BC>,
691    >(
692        core_ctx: &mut CC,
693        device: &CC::DeviceId,
694        addr: SpecifiedAddr<Ipv4Addr>,
695    ) -> AddressStatus<Ipv4PresentAddressStatus> {
696        if addr.is_limited_broadcast() {
697            return AddressStatus::Present(Ipv4PresentAddressStatus::LimitedBroadcast);
698        }
699
700        if MulticastAddr::new(addr.get())
701            .is_some_and(|addr| GmpQueryHandler::gmp_is_in_group(core_ctx, device, addr))
702        {
703            return AddressStatus::Present(Ipv4PresentAddressStatus::Multicast);
704        }
705
706        core_ctx.with_address_ids(device, |mut addrs, core_ctx| {
707            addrs
708                .find_map(|addr_id| {
709                    let dev_addr = addr_id.addr_sub();
710                    let (dev_addr, subnet) = dev_addr.addr_subnet();
711
712                    if **dev_addr == addr {
713                        let assigned = core_ctx.with_ip_address_data(
714                            device,
715                            &addr_id,
716                            |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| {
717                                *assigned
718                            },
719                        );
720
721                        if assigned {
722                            Some(AddressStatus::Present(Ipv4PresentAddressStatus::UnicastAssigned))
723                        } else {
724                            Some(AddressStatus::Present(Ipv4PresentAddressStatus::UnicastTentative))
725                        }
726                    } else if addr.get() == subnet.broadcast() {
727                        Some(AddressStatus::Present(Ipv4PresentAddressStatus::SubnetBroadcast))
728                    } else if device.is_loopback() && subnet.contains(addr.as_ref()) {
729                        Some(AddressStatus::Present(Ipv4PresentAddressStatus::LoopbackSubnet))
730                    } else {
731                        None
732                    }
733                })
734                .unwrap_or(AddressStatus::Unassigned)
735        })
736    }
737}
738
739impl AddressStatus<Ipv6PresentAddressStatus> {
740    /// /// Creates an IPv6 `AddressStatus` for `addr` on `device`.
741    pub fn from_context_addr_v6<
742        BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
743        CC: device::Ipv6DeviceContext<BC> + GmpQueryHandler<Ipv6, BC>,
744    >(
745        core_ctx: &mut CC,
746        device: &CC::DeviceId,
747        addr: SpecifiedAddr<Ipv6Addr>,
748    ) -> AddressStatus<Ipv6PresentAddressStatus> {
749        if MulticastAddr::new(addr.get())
750            .is_some_and(|addr| GmpQueryHandler::gmp_is_in_group(core_ctx, device, addr))
751        {
752            return AddressStatus::Present(Ipv6PresentAddressStatus::Multicast);
753        }
754
755        let addr_id = match core_ctx.get_address_id(device, addr) {
756            Ok(o) => o,
757            Err(NotFoundError) => return AddressStatus::Unassigned,
758        };
759
760        let assigned = core_ctx.with_ip_address_data(
761            device,
762            &addr_id,
763            |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
764        );
765
766        if assigned {
767            AddressStatus::Present(Ipv6PresentAddressStatus::UnicastAssigned)
768        } else {
769            AddressStatus::Present(Ipv6PresentAddressStatus::UnicastTentative)
770        }
771    }
772}
773
774impl<S: GenericOverIp<I>, I: Ip> GenericOverIp<I> for AddressStatus<S> {
775    type Type = AddressStatus<S::Type>;
776}
777
778/// The status of an IPv4 address.
779#[derive(Debug, PartialEq)]
780#[allow(missing_docs)]
781pub enum Ipv4PresentAddressStatus {
782    LimitedBroadcast,
783    SubnetBroadcast,
784    Multicast,
785    UnicastAssigned,
786    UnicastTentative,
787    /// This status indicates that the queried device was Loopback. The address
788    /// belongs to a subnet that is assigned to the interface. This status
789    /// takes lower precedence than `Unicast` and `SubnetBroadcast``, E.g. if
790    /// the loopback device is assigned `127.0.0.1/8`:
791    ///   * address `127.0.0.1` -> `Unicast`
792    ///   * address `127.0.0.2` -> `LoopbackSubnet`
793    ///   * address `127.255.255.255` -> `SubnetBroadcast`
794    /// This exists for Linux conformance, which on the Loopback device,
795    /// considers an IPv4 address assigned if it belongs to one of the device's
796    /// assigned subnets.
797    LoopbackSubnet,
798}
799
800impl Ipv4PresentAddressStatus {
801    fn to_broadcast_marker(&self) -> Option<<Ipv4 as BroadcastIpExt>::BroadcastMarker> {
802        match self {
803            Self::LimitedBroadcast | Self::SubnetBroadcast => Some(()),
804            Self::Multicast
805            | Self::UnicastAssigned
806            | Self::UnicastTentative
807            | Self::LoopbackSubnet => None,
808        }
809    }
810}
811
812/// The status of an IPv6 address.
813#[derive(Debug, PartialEq)]
814#[allow(missing_docs)]
815pub enum Ipv6PresentAddressStatus {
816    Multicast,
817    UnicastAssigned,
818    UnicastTentative,
819}
820
821/// An extension trait providing IP layer properties.
822pub trait IpLayerIpExt:
823    IpExt
824    + MulticastRouteIpExt
825    + IcmpHandlerIpExt
826    + FilterIpExt
827    + FragmentationIpExt
828    + IpDeviceIpExt
829    + IpCountersIpExt
830    + IcmpCountersIpExt
831    + ReassemblyIpExt
832{
833    /// IP Address status.
834    type AddressStatus: Debug;
835    /// IP Address state.
836    type State<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>: AsRef<
837        IpStateInner<Self, StrongDeviceId, BT>,
838    >;
839    /// State kept for packet identifiers.
840    type PacketIdState;
841    /// The type of a single packet identifier.
842    type PacketId;
843    /// Produces the next packet ID from the state.
844    fn next_packet_id_from_state(state: &Self::PacketIdState) -> Self::PacketId;
845}
846
847impl IpLayerIpExt for Ipv4 {
848    type AddressStatus = Ipv4PresentAddressStatus;
849    type State<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes> =
850        Ipv4State<StrongDeviceId, BT>;
851    type PacketIdState = AtomicU16;
852    type PacketId = u16;
853    fn next_packet_id_from_state(next_packet_id: &Self::PacketIdState) -> Self::PacketId {
854        // Relaxed ordering as we only need atomicity without synchronization. See
855        // https://en.cppreference.com/w/cpp/atomic/memory_order#Relaxed_ordering
856        // for more details.
857        next_packet_id.fetch_add(1, atomic::Ordering::Relaxed)
858    }
859}
860
861impl IpLayerIpExt for Ipv6 {
862    type AddressStatus = Ipv6PresentAddressStatus;
863    type State<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes> =
864        Ipv6State<StrongDeviceId, BT>;
865    type PacketIdState = ();
866    type PacketId = ();
867    fn next_packet_id_from_state((): &Self::PacketIdState) -> Self::PacketId {
868        ()
869    }
870}
871
872/// The state context provided to the IP layer.
873pub trait IpStateContext<I: IpLayerIpExt, BT: IpRoutingBindingsTypes + MatcherBindingsTypes>:
874    IpRouteTablesContext<I, BT, DeviceId: InterfaceProperties<BT::DeviceClass>>
875{
876    /// The context that provides access to the IP routing tables.
877    type IpRouteTablesCtx<'a>: IpRouteTablesContext<I, BT, DeviceId = Self::DeviceId>;
878
879    /// Gets an immutable reference to the rules table.
880    fn with_rules_table<
881        O,
882        F: FnOnce(&mut Self::IpRouteTablesCtx<'_>, &RulesTable<I, Self::DeviceId, BT>) -> O,
883    >(
884        &mut self,
885        cb: F,
886    ) -> O;
887
888    /// Gets a mutable reference to the rules table.
889    fn with_rules_table_mut<
890        O,
891        F: FnOnce(&mut Self::IpRouteTablesCtx<'_>, &mut RulesTable<I, Self::DeviceId, BT>) -> O,
892    >(
893        &mut self,
894        cb: F,
895    ) -> O;
896}
897
898/// The state context that gives access to routing tables provided to the IP layer.
899pub trait IpRouteTablesContext<I: IpLayerIpExt, BT: IpRoutingBindingsTypes>:
900    IpRouteTableContext<I, BT> + IpDeviceContext<I>
901{
902    /// The inner context that can provide access to individual routing tables.
903    type Ctx<'a>: IpRouteTableContext<I, BT, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
904
905    /// Gets the main table ID.
906    fn main_table_id(&self) -> RoutingTableId<I, Self::DeviceId, BT>;
907
908    /// Gets immutable access to all the routing tables that currently exist.
909    fn with_ip_routing_tables<
910        O,
911        F: FnOnce(
912            &mut Self::Ctx<'_>,
913            &HashMap<
914                RoutingTableId<I, Self::DeviceId, BT>,
915                PrimaryRc<BaseRoutingTableState<I, Self::DeviceId, BT>>,
916            >,
917        ) -> O,
918    >(
919        &mut self,
920        cb: F,
921    ) -> O;
922
923    /// Gets mutable access to all the routing tables that currently exist.
924    fn with_ip_routing_tables_mut<
925        O,
926        F: FnOnce(
927            &mut HashMap<
928                RoutingTableId<I, Self::DeviceId, BT>,
929                PrimaryRc<BaseRoutingTableState<I, Self::DeviceId, BT>>,
930            >,
931        ) -> O,
932    >(
933        &mut self,
934        cb: F,
935    ) -> O;
936
937    // TODO(https://fxbug.dev/354724171): Remove this function when we no longer
938    // make routing decisions starting from the main table.
939    /// Calls the function with an immutable reference to IP routing table.
940    fn with_main_ip_routing_table<
941        O,
942        F: FnOnce(&mut Self::IpDeviceIdCtx<'_>, &RoutingTable<I, Self::DeviceId>) -> O,
943    >(
944        &mut self,
945        cb: F,
946    ) -> O {
947        let main_table_id = self.main_table_id();
948        self.with_ip_routing_table(&main_table_id, cb)
949    }
950
951    // TODO(https://fxbug.dev/341194323): Remove this function when we no longer
952    // only update the main routing table by default.
953    /// Calls the function with a mutable reference to IP routing table.
954    fn with_main_ip_routing_table_mut<
955        O,
956        F: FnOnce(&mut Self::IpDeviceIdCtx<'_>, &mut RoutingTable<I, Self::DeviceId>) -> O,
957    >(
958        &mut self,
959        cb: F,
960    ) -> O {
961        let main_table_id = self.main_table_id();
962        self.with_ip_routing_table_mut(&main_table_id, cb)
963    }
964}
965
966/// The state context that gives access to a singular routing table.
967pub trait IpRouteTableContext<I: IpLayerIpExt, BT: IpRoutingBindingsTypes>:
968    IpDeviceContext<I>
969{
970    /// The inner device id context.
971    type IpDeviceIdCtx<'a>: DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
972        + IpRoutingDeviceContext<I>
973        + IpDeviceContext<I>;
974
975    /// Calls the function with an immutable reference to IP routing table.
976    fn with_ip_routing_table<
977        O,
978        F: FnOnce(&mut Self::IpDeviceIdCtx<'_>, &RoutingTable<I, Self::DeviceId>) -> O,
979    >(
980        &mut self,
981        table_id: &RoutingTableId<I, Self::DeviceId, BT>,
982        cb: F,
983    ) -> O;
984
985    /// Calls the function with a mutable reference to IP routing table.
986    fn with_ip_routing_table_mut<
987        O,
988        F: FnOnce(&mut Self::IpDeviceIdCtx<'_>, &mut RoutingTable<I, Self::DeviceId>) -> O,
989    >(
990        &mut self,
991        table_id: &RoutingTableId<I, Self::DeviceId, BT>,
992        cb: F,
993    ) -> O;
994}
995
996/// Provides access to an IP device's state for IP layer egress.
997pub trait IpDeviceEgressStateContext<I: IpLayerIpExt>: DeviceIdContext<AnyDevice> {
998    /// Calls the callback with the next packet ID.
999    fn with_next_packet_id<O, F: FnOnce(&I::PacketIdState) -> O>(&self, cb: F) -> O;
1000
1001    /// Returns the best local address for communicating with the remote.
1002    fn get_local_addr_for_remote(
1003        &mut self,
1004        device_id: &Self::DeviceId,
1005        remote: Option<SpecifiedAddr<I::Addr>>,
1006    ) -> Option<IpDeviceAddr<I::Addr>>;
1007
1008    /// Returns the hop limit.
1009    fn get_hop_limit(&mut self, device_id: &Self::DeviceId) -> NonZeroU8;
1010}
1011
1012/// Provides access to an IP device's state for IP layer ingress.
1013pub trait IpDeviceIngressStateContext<I: IpLayerIpExt>: DeviceIdContext<AnyDevice> {
1014    /// Gets the status of an address.
1015    ///
1016    /// Only the specified device will be checked for the address. Returns
1017    /// [`AddressStatus::Unassigned`] if the address is not assigned to the
1018    /// device.
1019    fn address_status_for_device(
1020        &mut self,
1021        addr: SpecifiedAddr<I::Addr>,
1022        device_id: &Self::DeviceId,
1023    ) -> AddressStatus<I::AddressStatus>;
1024}
1025
1026/// The IP device context provided to the IP layer.
1027pub trait IpDeviceContext<I: IpLayerIpExt>:
1028    IpDeviceEgressStateContext<I> + IpDeviceIngressStateContext<I>
1029{
1030    /// Is the device enabled?
1031    fn is_ip_device_enabled(&mut self, device_id: &Self::DeviceId) -> bool;
1032
1033    /// The iterator provided to [`IpDeviceContext::with_address_statuses`].
1034    type DeviceAndAddressStatusIter<'a>: Iterator<Item = (Self::DeviceId, I::AddressStatus)>;
1035
1036    /// Provides access to the status of an address.
1037    ///
1038    /// Calls the provided callback with an iterator over the devices for which
1039    /// the address is assigned and the status of the assignment for each
1040    /// device.
1041    fn with_address_statuses<F: FnOnce(Self::DeviceAndAddressStatusIter<'_>) -> R, R>(
1042        &mut self,
1043        addr: SpecifiedAddr<I::Addr>,
1044        cb: F,
1045    ) -> R;
1046
1047    /// Returns true iff the device has unicast forwarding enabled.
1048    fn is_device_unicast_forwarding_enabled(&mut self, device_id: &Self::DeviceId) -> bool;
1049}
1050
1051/// Provides the ability to check neighbor reachability via a specific device.
1052pub trait IpDeviceConfirmReachableContext<I: IpLayerIpExt, BC>: DeviceIdContext<AnyDevice> {
1053    /// Confirm transport-layer forward reachability to the specified neighbor
1054    /// through the specified device.
1055    fn confirm_reachable(
1056        &mut self,
1057        bindings_ctx: &mut BC,
1058        device: &Self::DeviceId,
1059        neighbor: SpecifiedAddr<I::Addr>,
1060    );
1061}
1062
1063/// Provides access to an IP device's MTU for the IP layer.
1064pub trait IpDeviceMtuContext<I: Ip>: DeviceIdContext<AnyDevice> {
1065    /// Returns the MTU of the device.
1066    ///
1067    /// The MTU is the maximum size of an IP packet.
1068    fn get_mtu(&mut self, device_id: &Self::DeviceId) -> Mtu;
1069}
1070
1071/// Events observed at the IP layer.
1072#[derive(Debug, Eq, Hash, PartialEq, GenericOverIp)]
1073#[generic_over_ip(I, Ip)]
1074pub enum IpLayerEvent<DeviceId, I: IpLayerIpExt> {
1075    /// A route needs to be added.
1076    AddRoute(types::AddableEntry<I::Addr, DeviceId>),
1077    /// Routes matching these specifiers need to be removed.
1078    RemoveRoutes {
1079        /// Destination subnet
1080        subnet: Subnet<I::Addr>,
1081        /// Outgoing interface
1082        device: DeviceId,
1083        /// Gateway/next-hop
1084        gateway: Option<SpecifiedAddr<I::Addr>>,
1085    },
1086    /// The multicast forwarding engine emitted an event.
1087    MulticastForwarding(MulticastForwardingEvent<I, DeviceId>),
1088}
1089
1090impl<DeviceId, I: IpLayerIpExt> From<MulticastForwardingEvent<I, DeviceId>>
1091    for IpLayerEvent<DeviceId, I>
1092{
1093    fn from(event: MulticastForwardingEvent<I, DeviceId>) -> IpLayerEvent<DeviceId, I> {
1094        IpLayerEvent::MulticastForwarding(event)
1095    }
1096}
1097
1098impl<DeviceId, I: IpLayerIpExt> IpLayerEvent<DeviceId, I> {
1099    /// Changes the device id type with `map`.
1100    pub fn map_device<N, F: Fn(DeviceId) -> N>(self, map: F) -> IpLayerEvent<N, I> {
1101        match self {
1102            IpLayerEvent::AddRoute(types::AddableEntry {
1103                subnet,
1104                device,
1105                gateway,
1106                metric,
1107                route_preference,
1108            }) => IpLayerEvent::AddRoute(types::AddableEntry {
1109                subnet,
1110                device: map(device),
1111                gateway,
1112                metric,
1113                route_preference,
1114            }),
1115            IpLayerEvent::RemoveRoutes { subnet, device, gateway } => {
1116                IpLayerEvent::RemoveRoutes { subnet, device: map(device), gateway }
1117            }
1118            IpLayerEvent::MulticastForwarding(e) => {
1119                IpLayerEvent::MulticastForwarding(e.map_device(map))
1120            }
1121        }
1122    }
1123}
1124
1125/// An event signifying a router advertisement has been received.
1126#[derive(Derivative, PartialEq, Eq, Clone, Hash)]
1127#[derivative(Debug)]
1128pub struct RouterAdvertisementEvent<D> {
1129    /// The raw bytes of the router advertisement message's options.
1130    // NB: avoid deriving Debug for this since it could contain PII.
1131    #[derivative(Debug = "ignore")]
1132    pub options_bytes: Box<[u8]>,
1133    /// The source address of the RA message.
1134    pub source: net_types::ip::Ipv6Addr,
1135    /// The device on which the message was received.
1136    pub device: D,
1137}
1138
1139impl<D> RouterAdvertisementEvent<D> {
1140    /// Maps the contained device ID type.
1141    pub fn map_device<N, F: Fn(D) -> N>(self, map: F) -> RouterAdvertisementEvent<N> {
1142        let Self { options_bytes, source, device } = self;
1143        RouterAdvertisementEvent { options_bytes, source, device: map(device) }
1144    }
1145}
1146
1147/// Ipv6-specific bindings execution context for the IP layer.
1148pub trait NdpBindingsContext<DeviceId>: EventContext<RouterAdvertisementEvent<DeviceId>> {}
1149impl<DeviceId, BC: EventContext<RouterAdvertisementEvent<DeviceId>>> NdpBindingsContext<DeviceId>
1150    for BC
1151{
1152}
1153
1154/// Defines how socket marks should be handled by the IP layer.
1155pub trait MarksBindingsContext {
1156    /// Mark domains for marks that should be kept when an egress packet is
1157    /// passed from the IP layer to the device. For egress packets that are
1158    /// delivered locally through the loopback interface, these marks are
1159    /// passed to the ingress path and can be observed by ingress filter hooks.
1160    fn marks_to_keep_on_egress() -> &'static [MarkDomain];
1161
1162    /// Mark domains for marks that should be copied to ingress packets. If
1163    /// early demux results in a socket then these marks are copied from the
1164    /// socket to the packet and can be observed in `LOCAL_INGRESS` filter
1165    /// hook.
1166    fn marks_to_set_on_ingress() -> &'static [MarkDomain];
1167
1168    /// Returns a copy of `packet_marks` with the mark domains in
1169    /// [`Self::marks_to_set_on_ingress`] overridden from `socket_marks`.
1170    fn update_ingress_marks(mut packet_marks: Marks, socket_marks: &Marks) -> Marks {
1171        for mark in Self::marks_to_set_on_ingress() {
1172            *packet_marks.get_mut(*mark) = *socket_marks.get(*mark);
1173        }
1174        packet_marks
1175    }
1176}
1177
1178/// The bindings execution context for the IP layer.
1179pub trait IpLayerBindingsContext<I: IpLayerIpExt, DeviceId>:
1180    InstantContext
1181    + EventContext<IpLayerEvent<DeviceId, I>>
1182    + FilterBindingsContext<DeviceId>
1183    + TxMetadataBindingsTypes
1184    + IpRoutingBindingsTypes
1185    + MarksBindingsContext
1186{
1187}
1188impl<
1189    I: IpLayerIpExt,
1190    DeviceId,
1191    BC: InstantContext
1192        + EventContext<IpLayerEvent<DeviceId, I>>
1193        + FilterBindingsContext<DeviceId>
1194        + TxMetadataBindingsTypes
1195        + IpRoutingBindingsTypes
1196        + MarksBindingsContext,
1197> IpLayerBindingsContext<I, DeviceId> for BC
1198{
1199}
1200
1201/// A marker trait for bindings types at the IP layer.
1202pub trait IpLayerBindingsTypes:
1203    IcmpBindingsTypes + IpStateBindingsTypes + IpRoutingBindingsTypes
1204{
1205}
1206impl<BT: IcmpBindingsTypes + IpStateBindingsTypes + IpRoutingBindingsTypes> IpLayerBindingsTypes
1207    for BT
1208{
1209}
1210
1211/// The execution context for the IP layer.
1212pub trait IpLayerContext<
1213    I: IpLayerIpExt,
1214    BC: IpLayerBindingsContext<I, <Self as DeviceIdContext<AnyDevice>>::DeviceId>,
1215>:
1216    IpStateContext<I, BC>
1217    + IpDeviceContext<I>
1218    + IpDeviceMtuContext<I>
1219    + IpDeviceSendContext<I, BC>
1220    + IcmpErrorHandler<I, BC>
1221    + MulticastForwardingStateContext<I, BC>
1222    + MulticastForwardingDeviceContext<I>
1223    + CounterContext<MulticastForwardingCounters<I>>
1224    + ResourceCounterContext<<Self as DeviceIdContext<AnyDevice>>::DeviceId, IpCounters<I>>
1225{
1226}
1227
1228impl<
1229    I: IpLayerIpExt,
1230    BC: IpLayerBindingsContext<I, <CC as DeviceIdContext<AnyDevice>>::DeviceId>,
1231    CC: IpStateContext<I, BC>
1232        + IpDeviceContext<I>
1233        + IpDeviceMtuContext<I>
1234        + IpDeviceSendContext<I, BC>
1235        + IcmpErrorHandler<I, BC>
1236        + MulticastForwardingStateContext<I, BC>
1237        + MulticastForwardingDeviceContext<I>
1238        + CounterContext<MulticastForwardingCounters<I>>
1239        + ResourceCounterContext<<Self as DeviceIdContext<AnyDevice>>::DeviceId, IpCounters<I>>,
1240> IpLayerContext<I, BC> for CC
1241{
1242}
1243
1244fn is_unicast_assigned<I: IpLayerIpExt>(status: &I::AddressStatus) -> bool {
1245    #[derive(GenericOverIp)]
1246    #[generic_over_ip(I, Ip)]
1247    struct WrapAddressStatus<'a, I: IpLayerIpExt>(&'a I::AddressStatus);
1248
1249    I::map_ip(
1250        WrapAddressStatus(status),
1251        |WrapAddressStatus(status)| match status {
1252            Ipv4PresentAddressStatus::UnicastAssigned
1253            | Ipv4PresentAddressStatus::LoopbackSubnet => true,
1254            Ipv4PresentAddressStatus::UnicastTentative
1255            | Ipv4PresentAddressStatus::LimitedBroadcast
1256            | Ipv4PresentAddressStatus::SubnetBroadcast
1257            | Ipv4PresentAddressStatus::Multicast => false,
1258        },
1259        |WrapAddressStatus(status)| match status {
1260            Ipv6PresentAddressStatus::UnicastAssigned => true,
1261            Ipv6PresentAddressStatus::Multicast | Ipv6PresentAddressStatus::UnicastTentative => {
1262                false
1263            }
1264        },
1265    )
1266}
1267
1268fn is_local_assigned_address<I: Ip + IpLayerIpExt, CC: IpDeviceIngressStateContext<I>>(
1269    core_ctx: &mut CC,
1270    device: &CC::DeviceId,
1271    addr: IpDeviceAddr<I::Addr>,
1272) -> bool {
1273    match core_ctx.address_status_for_device(addr.into(), device) {
1274        AddressStatus::Present(status) => is_unicast_assigned::<I>(&status),
1275        AddressStatus::Unassigned => false,
1276    }
1277}
1278
1279fn get_device_with_assigned_address<I, CC>(
1280    core_ctx: &mut CC,
1281    addr: IpDeviceAddr<I::Addr>,
1282) -> Option<(CC::DeviceId, I::AddressStatus)>
1283where
1284    I: IpLayerIpExt,
1285    CC: IpDeviceContext<I>,
1286{
1287    core_ctx.with_address_statuses(addr.into(), |mut it| {
1288        it.find_map(|(device, status)| {
1289            is_unicast_assigned::<I>(&status).then_some((device, status))
1290        })
1291    })
1292}
1293
1294// Returns the local IP address to use for sending packets from the
1295// given device to `addr`, restricting to `local_ip` if it is not
1296// `None`.
1297fn get_local_addr<I: Ip + IpLayerIpExt, CC: IpDeviceContext<I>>(
1298    core_ctx: &mut CC,
1299    local_ip_and_policy: Option<(IpDeviceAddr<I::Addr>, NonLocalSrcAddrPolicy)>,
1300    device: &CC::DeviceId,
1301    remote_addr: Option<RoutableIpAddr<I::Addr>>,
1302) -> Result<IpDeviceAddr<I::Addr>, ResolveRouteError> {
1303    match local_ip_and_policy {
1304        Some((local_ip, NonLocalSrcAddrPolicy::Allow)) => Ok(local_ip),
1305        Some((local_ip, NonLocalSrcAddrPolicy::Deny)) => {
1306            is_local_assigned_address(core_ctx, device, local_ip)
1307                .then_some(local_ip)
1308                .ok_or(ResolveRouteError::NoSrcAddr)
1309        }
1310        None => core_ctx
1311            .get_local_addr_for_remote(device, remote_addr.map(Into::into))
1312            .ok_or(ResolveRouteError::NoSrcAddr),
1313    }
1314}
1315
1316/// An error occurred while resolving the route to a destination
1317#[derive(Error, Copy, Clone, Debug, Eq, GenericOverIp, PartialEq)]
1318#[generic_over_ip()]
1319pub enum ResolveRouteError {
1320    /// A source address could not be selected.
1321    #[error("a source address could not be selected")]
1322    NoSrcAddr,
1323    /// The destination in unreachable.
1324    #[error("no route exists to the destination IP address")]
1325    Unreachable,
1326}
1327
1328/// Like [`get_local_addr`], but willing to forward internally as necessary.
1329fn get_local_addr_with_internal_forwarding<I, CC>(
1330    core_ctx: &mut CC,
1331    local_ip_and_policy: Option<(IpDeviceAddr<I::Addr>, NonLocalSrcAddrPolicy)>,
1332    device: &CC::DeviceId,
1333    remote_addr: Option<RoutableIpAddr<I::Addr>>,
1334) -> Result<(IpDeviceAddr<I::Addr>, InternalForwarding<CC::DeviceId>), ResolveRouteError>
1335where
1336    I: IpLayerIpExt,
1337    CC: IpDeviceContext<I>,
1338{
1339    match get_local_addr(core_ctx, local_ip_and_policy, device, remote_addr) {
1340        Ok(src_addr) => Ok((src_addr, InternalForwarding::NotUsed)),
1341        Err(e) => {
1342            // If a local_ip was specified, the local_ip is assigned to a
1343            // device, and that device has forwarding enabled, use internal
1344            // forwarding.
1345            //
1346            // This enables a weak host model when the Netstack is configured as
1347            // a router. Conceptually the netstack is forwarding the packet from
1348            // the local IP's device to the output device of the selected route.
1349            if let Some((local_ip, _policy)) = local_ip_and_policy {
1350                if let Some((device, _addr_status)) =
1351                    get_device_with_assigned_address(core_ctx, local_ip)
1352                {
1353                    if core_ctx.is_device_unicast_forwarding_enabled(&device) {
1354                        return Ok((local_ip, InternalForwarding::Used(device)));
1355                    }
1356                }
1357            }
1358            Err(e)
1359        }
1360    }
1361}
1362
1363/// The information about the rule walk in addition to a custom state. This type is introduced so
1364/// that `walk_rules` can be extended later with more information about the walk if needed.
1365#[derive(Debug, PartialEq, Eq)]
1366struct RuleWalkInfo<O> {
1367    /// Whether there is a rule with a source address matcher during the walk.
1368    observed_source_address_matcher: bool,
1369    /// The custom info carried. For example this could be the lookup result from the user provided
1370    /// function.
1371    inner: O,
1372}
1373
1374/// A helper function that traverses through the rules table.
1375///
1376/// To walk through the rules, you need to provide it with an initial value for the loop and a
1377/// callback function that yieds a [`ControlFlow`] result to indicate whether the traversal should
1378/// stop.
1379///
1380/// # Returns
1381///
1382/// - `ControlFlow::Break(RuleAction::Lookup(_))` if we hit a lookup rule and an output is
1383///   yielded from the route table.
1384/// - `ControlFlow::Break(RuleAction::Unreachable)` if we hit an unreachable rule.
1385/// - `ControlFlow::Continue(_)` if we finished walking the rules table without yielding any
1386///   result.
1387fn walk_rules<
1388    I: IpLayerIpExt,
1389    BT: IpRoutingBindingsTypes + MatcherBindingsTypes,
1390    CC: IpRouteTablesContext<I, BT, DeviceId: InterfaceProperties<BT::DeviceClass>>,
1391    O,
1392    State,
1393    F: FnMut(
1394        State,
1395        &mut CC::IpDeviceIdCtx<'_>,
1396        &RoutingTable<I, CC::DeviceId>,
1397    ) -> ControlFlow<O, State>,
1398>(
1399    core_ctx: &mut CC,
1400    rules: &RulesTable<I, CC::DeviceId, BT>,
1401    init: State,
1402    rule_input: &RuleInput<'_, I, CC::DeviceId>,
1403    mut lookup_table: F,
1404) -> ControlFlow<RuleAction<RuleWalkInfo<O>>, RuleWalkInfo<State>> {
1405    rules.iter().try_fold(
1406        RuleWalkInfo { inner: init, observed_source_address_matcher: false },
1407        |RuleWalkInfo { inner: state, observed_source_address_matcher },
1408         Rule { action, matcher }| {
1409            let observed_source_address_matcher =
1410                observed_source_address_matcher || matcher.source_address_matcher.is_some();
1411            if !matcher.matches(rule_input) {
1412                return ControlFlow::Continue(RuleWalkInfo {
1413                    inner: state,
1414                    observed_source_address_matcher,
1415                });
1416            }
1417            match action {
1418                RuleAction::Unreachable => return ControlFlow::Break(RuleAction::Unreachable),
1419                RuleAction::Lookup(table_id) => core_ctx.with_ip_routing_table(
1420                    &table_id,
1421                    |core_ctx, table| match lookup_table(state, core_ctx, table) {
1422                        ControlFlow::Break(out) => {
1423                            ControlFlow::Break(RuleAction::Lookup(RuleWalkInfo {
1424                                inner: out,
1425                                observed_source_address_matcher,
1426                            }))
1427                        }
1428                        ControlFlow::Continue(state) => ControlFlow::Continue(RuleWalkInfo {
1429                            inner: state,
1430                            observed_source_address_matcher,
1431                        }),
1432                    },
1433                ),
1434            }
1435        },
1436    )
1437}
1438
1439/// Returns the outgoing routing instructions for reaching the given destination.
1440///
1441/// If a `device` is specified, the resolved route is limited to those that
1442/// egress over the device.
1443///
1444/// If `src_ip` is specified the resolved route is limited to those that egress
1445/// over a device with the address assigned.
1446///
1447/// This function should only be used for calculating a route for an outgoing packet
1448/// that is generated by us.
1449pub fn resolve_output_route_to_destination<
1450    I: Ip + IpDeviceStateIpExt + IpDeviceIpExt + IpLayerIpExt,
1451    BC: IpDeviceBindingsContext<I, CC::DeviceId> + IpLayerBindingsContext<I, CC::DeviceId>,
1452    CC: IpStateContext<I, BC> + IpDeviceContext<I> + device::IpDeviceConfigurationContext<I, BC>,
1453>(
1454    core_ctx: &mut CC,
1455    device: Option<&CC::DeviceId>,
1456    src_ip_and_policy: Option<(IpDeviceAddr<I::Addr>, NonLocalSrcAddrPolicy)>,
1457    dst_ip: Option<RoutableIpAddr<I::Addr>>,
1458    marks: &Marks,
1459) -> Result<ResolvedRoute<I, CC::DeviceId>, ResolveRouteError> {
1460    enum LocalDelivery<A, D> {
1461        WeakLoopback { dst_ip: A, device: D },
1462        StrongForDevice(D),
1463    }
1464
1465    // Check if locally destined. If the destination is an address assigned on
1466    // an interface, and an egress interface wasn't specifically selected, route
1467    // via the loopback device. This lets us operate as a strong host when an
1468    // outgoing interface is explicitly requested while still enabling local
1469    // delivery via the loopback interface, which is acting as a weak host. Note
1470    // that if the loopback interface is requested as an outgoing interface,
1471    // route selection is still performed as a strong host! This makes the
1472    // loopback interface behave more like the other interfaces on the system.
1473    //
1474    // TODO(https://fxbug.dev/42065870): Encode the delivery of locally-
1475    // destined packets to loopback in the route table.
1476    //
1477    // TODO(https://fxbug.dev/322539434): Linux is more permissive about
1478    // allowing cross-device local delivery even when SO_BINDTODEVICE or
1479    // link-local addresses are involved, and this behavior may need to be
1480    // emulated.
1481    let local_delivery_instructions: Option<LocalDelivery<IpDeviceAddr<I::Addr>, CC::DeviceId>> = {
1482        let dst_ip = dst_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr);
1483        match (device, dst_ip) {
1484            (Some(device), Some(dst_ip)) => is_local_assigned_address(core_ctx, device, dst_ip)
1485                .then_some(LocalDelivery::StrongForDevice(device.clone())),
1486            (None, Some(dst_ip)) => {
1487                get_device_with_assigned_address(core_ctx, dst_ip).map(
1488                    |(dst_device, _addr_status)| {
1489                        // If either the source or destination addresses needs
1490                        // a zone ID, then use strong host to enforce that the
1491                        // source and destination addresses are assigned to the
1492                        // same interface.
1493                        if src_ip_and_policy
1494                            .is_some_and(|(ip, _policy)| ip.as_ref().must_have_zone())
1495                            || dst_ip.as_ref().must_have_zone()
1496                        {
1497                            LocalDelivery::StrongForDevice(dst_device)
1498                        } else {
1499                            LocalDelivery::WeakLoopback { dst_ip, device: dst_device }
1500                        }
1501                    },
1502                )
1503            }
1504            (_, None) => None,
1505        }
1506    };
1507
1508    if let Some(local_delivery) = local_delivery_instructions {
1509        let loopback = core_ctx.loopback_id().ok_or(ResolveRouteError::Unreachable)?;
1510
1511        let (src_addr, dest_device) = match local_delivery {
1512            LocalDelivery::WeakLoopback { dst_ip, device } => {
1513                let src_ip = match src_ip_and_policy {
1514                    Some((src_ip, NonLocalSrcAddrPolicy::Deny)) => {
1515                        let _device = get_device_with_assigned_address(core_ctx, src_ip)
1516                            .ok_or(ResolveRouteError::NoSrcAddr)?;
1517                        src_ip
1518                    }
1519                    Some((src_ip, NonLocalSrcAddrPolicy::Allow)) => src_ip,
1520                    None => dst_ip,
1521                };
1522                (src_ip, device)
1523            }
1524            LocalDelivery::StrongForDevice(device) => {
1525                (get_local_addr(core_ctx, src_ip_and_policy, &device, dst_ip)?, device)
1526            }
1527        };
1528        return Ok(ResolvedRoute {
1529            src_addr,
1530            local_delivery_device: Some(dest_device),
1531            device: loopback,
1532            next_hop: NextHop::RemoteAsNeighbor,
1533            internal_forwarding: InternalForwarding::NotUsed,
1534        });
1535    }
1536    let bound_address = src_ip_and_policy.map(|(sock_addr, _policy)| sock_addr.into_inner().get());
1537    let rule_input = RuleInput {
1538        packet_origin: PacketOrigin::Local { bound_address, bound_device: device },
1539        marks,
1540    };
1541    core_ctx.with_rules_table(|core_ctx, rules: &RulesTable<_, _, BC>| {
1542        let mut walk_rules = |rule_input, src_ip_and_policy| {
1543            walk_rules(
1544                core_ctx,
1545                rules,
1546                None, /* first error encountered */
1547                rule_input,
1548                |first_error, core_ctx, table| {
1549                    let mut matching_with_addr = table.lookup_filter_map(
1550                        core_ctx,
1551                        device,
1552                        dst_ip.map_or(I::UNSPECIFIED_ADDRESS, |a| a.addr()),
1553                        |core_ctx, d| {
1554                            Some(get_local_addr_with_internal_forwarding(
1555                                core_ctx,
1556                                src_ip_and_policy,
1557                                d,
1558                                dst_ip,
1559                            ))
1560                        },
1561                    );
1562
1563                    let first_error_in_this_table = match matching_with_addr.next() {
1564                        Some((
1565                            Destination { device, next_hop },
1566                            Ok((local_addr, internal_forwarding)),
1567                        )) => {
1568                            return ControlFlow::Break(Ok((
1569                                Destination { device: device.clone(), next_hop },
1570                                local_addr,
1571                                internal_forwarding,
1572                            )));
1573                        }
1574                        Some((_, Err(e))) => e,
1575                        // Note: rule evaluation will continue on to the next rule, if the
1576                        // previous rule was `Lookup` but the table didn't have the route
1577                        // inside of it.
1578                        None => return ControlFlow::Continue(first_error),
1579                    };
1580
1581                    matching_with_addr
1582                        .filter_map(|(destination, local_addr)| {
1583                            // Select successful routes. We ignore later errors
1584                            // since we've already saved the first one.
1585                            local_addr.ok_checked::<ResolveRouteError>().map(
1586                                |(local_addr, internal_forwarding)| {
1587                                    (destination, local_addr, internal_forwarding)
1588                                },
1589                            )
1590                        })
1591                        .next()
1592                        .map_or(
1593                            ControlFlow::Continue(first_error.or(Some(first_error_in_this_table))),
1594                            |(
1595                                Destination { device, next_hop },
1596                                local_addr,
1597                                internal_forwarding,
1598                            )| {
1599                                ControlFlow::Break(Ok((
1600                                    Destination { device: device.clone(), next_hop },
1601                                    local_addr,
1602                                    internal_forwarding,
1603                                )))
1604                            },
1605                        )
1606                },
1607            )
1608        };
1609
1610        let result = match walk_rules(&rule_input, src_ip_and_policy) {
1611            // Only try to resolve a route again if all of the following are true:
1612            // 1. The source address is not provided by the caller.
1613            // 2. A route is successfully resolved so we selected a source address.
1614            // 3. There is a rule with a source address matcher during the resolution.
1615            // The rationale is to make sure the route resolution converges to a sensible route
1616            // after considering the source address we select.
1617            ControlFlow::Break(RuleAction::Lookup(RuleWalkInfo {
1618                inner: Ok((_dst, selected_src_addr, _internal_forwarding)),
1619                observed_source_address_matcher: true,
1620            })) if src_ip_and_policy.is_none() => walk_rules(
1621                &RuleInput {
1622                    packet_origin: PacketOrigin::Local {
1623                        bound_address: Some(selected_src_addr.into()),
1624                        bound_device: device,
1625                    },
1626                    marks,
1627                },
1628                Some((selected_src_addr, NonLocalSrcAddrPolicy::Deny)),
1629            ),
1630            result => result,
1631        };
1632
1633        match result {
1634            ControlFlow::Break(RuleAction::Lookup(RuleWalkInfo {
1635                inner: result,
1636                observed_source_address_matcher: _,
1637            })) => {
1638                result.map(|(Destination { device, next_hop }, src_addr, internal_forwarding)| {
1639                    ResolvedRoute {
1640                        src_addr,
1641                        device,
1642                        local_delivery_device: None,
1643                        next_hop,
1644                        internal_forwarding,
1645                    }
1646                })
1647            }
1648            ControlFlow::Break(RuleAction::Unreachable) => Err(ResolveRouteError::Unreachable),
1649            ControlFlow::Continue(RuleWalkInfo {
1650                inner: first_error,
1651                observed_source_address_matcher: _,
1652            }) => Err(first_error.unwrap_or(ResolveRouteError::Unreachable)),
1653        }
1654    })
1655}
1656
1657/// Enables a blanket implementation of [`IpSocketContext`].
1658///
1659/// Implementing this marker trait for a type enables a blanket implementation
1660/// of `IpSocketContext` given the other requirements are met.
1661pub trait UseIpSocketContextBlanket {}
1662
1663impl<I, BC, CC> IpSocketContext<I, BC> for CC
1664where
1665    I: Ip + IpDeviceStateIpExt + IpDeviceIpExt + IpLayerIpExt,
1666    BC: IpDeviceBindingsContext<I, CC::DeviceId>
1667        + IpLayerBindingsContext<I, CC::DeviceId>
1668        + IpSocketBindingsContext<CC::DeviceId>,
1669    CC: IpLayerEgressContext<I, BC>
1670        + IpStateContext<I, BC>
1671        + IpDeviceContext<I>
1672        + IpDeviceConfirmReachableContext<I, BC>
1673        + IpDeviceMtuContext<I>
1674        + device::IpDeviceConfigurationContext<I, BC>
1675        + IcmpErrorHandler<I, BC>
1676        + UseIpSocketContextBlanket,
1677{
1678    fn lookup_route(
1679        &mut self,
1680        _bindings_ctx: &mut BC,
1681        device: Option<&CC::DeviceId>,
1682        local_ip: Option<IpDeviceAddr<I::Addr>>,
1683        addr: RoutableIpAddr<I::Addr>,
1684        transparent: bool,
1685        marks: &Marks,
1686    ) -> Result<ResolvedRoute<I, CC::DeviceId>, ResolveRouteError> {
1687        let src_ip_and_policy = local_ip.map(|local_ip| {
1688            (
1689                local_ip,
1690                if transparent {
1691                    NonLocalSrcAddrPolicy::Allow
1692                } else {
1693                    NonLocalSrcAddrPolicy::Deny
1694                },
1695            )
1696        });
1697        let res =
1698            resolve_output_route_to_destination(self, device, src_ip_and_policy, Some(addr), marks);
1699        trace!(
1700            "lookup_route(\
1701                device={device:?}, \
1702                local_ip={local_ip:?}, \
1703                addr={addr:?}, \
1704                transparent={transparent:?}, \
1705                marks={marks:?}) => {res:?}"
1706        );
1707        res
1708    }
1709
1710    fn send_ip_packet<S>(
1711        &mut self,
1712        bindings_ctx: &mut BC,
1713        meta: SendIpPacketMeta<
1714            I,
1715            &<CC as DeviceIdContext<AnyDevice>>::DeviceId,
1716            SpecifiedAddr<I::Addr>,
1717        >,
1718        body: S,
1719        packet_metadata: IpLayerPacketMetadata<I, CC::WeakAddressId, BC>,
1720    ) -> Result<(), IpSendFrameError<S>>
1721    where
1722        S: TransportPacketSerializer<I>,
1723        S::Buffer: BufferMut,
1724    {
1725        send_ip_packet_from_device(self, bindings_ctx, meta.into(), body, packet_metadata)
1726    }
1727
1728    fn get_loopback_device(&mut self) -> Option<Self::DeviceId> {
1729        device::IpDeviceConfigurationContext::<I, _>::loopback_id(self)
1730    }
1731
1732    fn confirm_reachable(
1733        &mut self,
1734        bindings_ctx: &mut BC,
1735        dst: SpecifiedAddr<I::Addr>,
1736        input: RuleInput<'_, I, Self::DeviceId>,
1737    ) {
1738        match lookup_route_table(self, dst.get(), input) {
1739            Some(Destination { next_hop, device }) => {
1740                let neighbor = match next_hop {
1741                    NextHop::RemoteAsNeighbor => dst,
1742                    NextHop::Gateway(gateway) => gateway,
1743                    NextHop::Broadcast(marker) => {
1744                        I::map_ip::<_, ()>(
1745                            WrapBroadcastMarker(marker),
1746                            |WrapBroadcastMarker(())| {
1747                                debug!(
1748                                    "can't confirm {dst:?}@{device:?} as reachable: \
1749                                    dst is a broadcast address"
1750                                );
1751                            },
1752                            |WrapBroadcastMarker(never)| match never {},
1753                        );
1754                        return;
1755                    }
1756                };
1757                IpDeviceConfirmReachableContext::confirm_reachable(
1758                    self,
1759                    bindings_ctx,
1760                    &device,
1761                    neighbor,
1762                );
1763            }
1764            None => {
1765                debug!("can't confirm {dst:?} as reachable: no route");
1766            }
1767        }
1768    }
1769}
1770
1771/// Trait that provides basic socket information for types that carry a socket
1772/// ID.
1773pub trait SocketMetadata<CC>
1774where
1775    CC: ?Sized,
1776{
1777    /// Returns the SocketInfo for the socket.
1778    fn socket_info(&self, core_ctx: &mut CC) -> SocketInfo;
1779    /// Returns Socket Marks.
1780    fn marks(&self, _core_ctx: &mut CC) -> Marks;
1781}
1782
1783impl<T, O, CC> SocketMetadata<CC> for EitherStack<T, O>
1784where
1785    CC: ?Sized,
1786    T: SocketMetadata<CC>,
1787    O: SocketMetadata<CC>,
1788{
1789    fn socket_info(&self, core_ctx: &mut CC) -> SocketInfo {
1790        match self {
1791            Self::ThisStack(t) => t.socket_info(core_ctx),
1792            Self::OtherStack(o) => o.socket_info(core_ctx),
1793        }
1794    }
1795
1796    fn marks(&self, core_ctx: &mut CC) -> Marks {
1797        match self {
1798            Self::ThisStack(t) => t.marks(core_ctx),
1799            Self::OtherStack(o) => o.marks(core_ctx),
1800        }
1801    }
1802}
1803
1804/// The IP context providing dispatch to the available transport protocols.
1805///
1806/// This trait acts like a demux on the transport protocol for ingress IP
1807/// packets.
1808pub trait IpTransportDispatchContext<I: IpLayerIpExt, BC>: DeviceIdContext<AnyDevice> {
1809    /// Early Demux result.
1810    type EarlyDemuxSocket: SocketMetadata<Self>;
1811
1812    /// Performs early demux result.
1813    fn early_demux<B: ParseBuffer>(
1814        &mut self,
1815        device: &Self::DeviceId,
1816        frame_dst: Option<LocalFrameDestination>,
1817        src_ip: I::Addr,
1818        dst_ip: I::Addr,
1819        proto: I::Proto,
1820        body: B,
1821    ) -> Option<Self::EarlyDemuxSocket>;
1822
1823    /// Dispatches a received incoming IP packet to the appropriate protocol.
1824    /// In case of a failure returns the kind of the ICMP error that should be
1825    /// sent back to the source.
1826    fn dispatch_receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
1827        &mut self,
1828        bindings_ctx: &mut BC,
1829        device: &Self::DeviceId,
1830        src_ip: I::RecvSrcAddr,
1831        dst_ip: SpecifiedAddr<I::Addr>,
1832        proto: I::Proto,
1833        body: B,
1834        info: &mut LocalDeliveryPacketInfo<I, H>,
1835        early_demux_socket: Option<Self::EarlyDemuxSocket>,
1836    ) -> Result<(), I::IcmpError>;
1837}
1838
1839/// A marker trait for all the contexts required for IP ingress.
1840pub trait IpLayerIngressContext<I: IpLayerIpExt, BC: IpLayerBindingsContext<I, Self::DeviceId>>:
1841    IpTransportDispatchContext<
1842        I,
1843        BC,
1844        DeviceId: netstack3_base::InterfaceProperties<BC::DeviceClass>,
1845    > + IpDeviceIngressStateContext<I>
1846    + IpDeviceMtuContext<I>
1847    + IpDeviceSendContext<I, BC>
1848    + IcmpErrorHandler<I, BC>
1849    + IpLayerContext<I, BC>
1850    + FragmentHandler<I, BC>
1851    + FilterHandlerProvider<I, BC>
1852    + RawIpSocketHandler<I, BC>
1853{
1854}
1855
1856impl<
1857    I: IpLayerIpExt,
1858    BC: IpLayerBindingsContext<I, CC::DeviceId>,
1859    CC: IpTransportDispatchContext<
1860            I,
1861            BC,
1862            DeviceId: netstack3_base::InterfaceProperties<BC::DeviceClass>,
1863        > + IpDeviceIngressStateContext<I>
1864        + IpDeviceMtuContext<I>
1865        + IpDeviceSendContext<I, BC>
1866        + IcmpErrorHandler<I, BC>
1867        + IpLayerContext<I, BC>
1868        + FragmentHandler<I, BC>
1869        + FilterHandlerProvider<I, BC>
1870        + RawIpSocketHandler<I, BC>,
1871> IpLayerIngressContext<I, BC> for CC
1872{
1873}
1874
1875/// A marker trait for all the contexts required for IP egress.
1876pub trait IpLayerEgressContext<I, BC>:
1877    IpDeviceSendContext<I, BC, DeviceId: netstack3_base::InterfaceProperties<BC::DeviceClass>>
1878    + FilterHandlerProvider<I, BC>
1879    + ResourceCounterContext<Self::DeviceId, IpCounters<I>>
1880where
1881    I: IpLayerIpExt,
1882    BC: FilterBindingsContext<Self::DeviceId> + TxMetadataBindingsTypes,
1883{
1884}
1885
1886impl<I, BC, CC> IpLayerEgressContext<I, BC> for CC
1887where
1888    I: IpLayerIpExt,
1889    BC: FilterBindingsContext<CC::DeviceId> + TxMetadataBindingsTypes,
1890    CC: IpDeviceSendContext<I, BC, DeviceId: netstack3_base::InterfaceProperties<BC::DeviceClass>>
1891        + FilterHandlerProvider<I, BC>
1892        + ResourceCounterContext<Self::DeviceId, IpCounters<I>>,
1893{
1894}
1895
1896/// A marker trait for all the contexts required for IP forwarding.
1897pub trait IpLayerForwardingContext<I: IpLayerIpExt, BC: IpLayerBindingsContext<I, Self::DeviceId>>:
1898    IpLayerEgressContext<I, BC> + IcmpErrorHandler<I, BC> + IpDeviceMtuContext<I>
1899{
1900}
1901
1902impl<
1903    I: IpLayerIpExt,
1904    BC: IpLayerBindingsContext<I, CC::DeviceId>,
1905    CC: IpLayerEgressContext<I, BC> + IcmpErrorHandler<I, BC> + IpDeviceMtuContext<I>,
1906> IpLayerForwardingContext<I, BC> for CC
1907{
1908}
1909
1910/// A builder for IPv4 state.
1911#[derive(Copy, Clone, Default)]
1912pub struct Ipv4StateBuilder {
1913    icmp: Icmpv4StateBuilder,
1914}
1915
1916impl Ipv4StateBuilder {
1917    /// Get the builder for the ICMPv4 state.
1918    #[cfg(any(test, feature = "testutils"))]
1919    pub fn icmpv4_builder(&mut self) -> &mut Icmpv4StateBuilder {
1920        &mut self.icmp
1921    }
1922
1923    /// Builds the [`Ipv4State`].
1924    pub fn build<
1925        CC: CoreTimerContext<IpLayerTimerId, BC>,
1926        StrongDeviceId: StrongDeviceIdentifier,
1927        BC: TimerContext + RngContext + IpLayerBindingsTypes,
1928    >(
1929        self,
1930        bindings_ctx: &mut BC,
1931    ) -> Ipv4State<StrongDeviceId, BC> {
1932        let Ipv4StateBuilder { icmp } = self;
1933
1934        Ipv4State {
1935            inner: IpStateInner::new::<CC>(bindings_ctx),
1936            icmp: icmp.build(),
1937            next_packet_id: Default::default(),
1938        }
1939    }
1940}
1941
1942/// A builder for IPv6 state.
1943///
1944/// By default, opaque IIDs will not be used to generate stable SLAAC addresses.
1945#[derive(Copy, Clone)]
1946pub struct Ipv6StateBuilder {
1947    icmp: Icmpv6StateBuilder,
1948    slaac_stable_secret_key: Option<IidSecret>,
1949}
1950
1951impl Ipv6StateBuilder {
1952    /// Sets the secret key used to generate stable SLAAC addresses.
1953    ///
1954    /// If `slaac_stable_secret_key` is left unset, opaque IIDs will not be used to
1955    /// generate stable SLAAC addresses.
1956    pub fn slaac_stable_secret_key(&mut self, secret_key: IidSecret) -> &mut Self {
1957        self.slaac_stable_secret_key = Some(secret_key);
1958        self
1959    }
1960
1961    /// Builds the [`Ipv6State`].
1962    ///
1963    /// # Panics
1964    ///
1965    /// Panics if the `slaac_stable_secret_key` has not been set.
1966    pub fn build<
1967        CC: CoreTimerContext<IpLayerTimerId, BC>,
1968        StrongDeviceId: StrongDeviceIdentifier,
1969        BC: TimerContext + RngContext + IpLayerBindingsTypes,
1970    >(
1971        self,
1972        bindings_ctx: &mut BC,
1973    ) -> Ipv6State<StrongDeviceId, BC> {
1974        let Ipv6StateBuilder { icmp, slaac_stable_secret_key } = self;
1975
1976        let slaac_stable_secret_key = slaac_stable_secret_key
1977            .expect("stable SLAAC secret key was not provided to `Ipv6StateBuilder`");
1978
1979        Ipv6State {
1980            inner: IpStateInner::new::<CC>(bindings_ctx),
1981            icmp: icmp.build(),
1982            slaac_counters: Default::default(),
1983            slaac_temp_secret_key: IidSecret::new_random(&mut bindings_ctx.rng()),
1984            slaac_stable_secret_key,
1985        }
1986    }
1987}
1988
1989impl Default for Ipv6StateBuilder {
1990    fn default() -> Self {
1991        #[cfg(any(test, feature = "testutils"))]
1992        let slaac_stable_secret_key = Some(IidSecret::ALL_ONES);
1993
1994        #[cfg(not(any(test, feature = "testutils")))]
1995        let slaac_stable_secret_key = None;
1996
1997        Self { icmp: Icmpv6StateBuilder::default(), slaac_stable_secret_key }
1998    }
1999}
2000
2001/// The stack's IPv4 state.
2002pub struct Ipv4State<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes> {
2003    /// The common inner IP layer state.
2004    pub inner: IpStateInner<Ipv4, StrongDeviceId, BT>,
2005    /// The ICMP state.
2006    pub icmp: Icmpv4State<BT>,
2007    /// The atomic counter providing IPv4 packet identifiers.
2008    pub next_packet_id: AtomicU16,
2009}
2010
2011impl<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2012    AsRef<IpStateInner<Ipv4, StrongDeviceId, BT>> for Ipv4State<StrongDeviceId, BT>
2013{
2014    fn as_ref(&self) -> &IpStateInner<Ipv4, StrongDeviceId, BT> {
2015        &self.inner
2016    }
2017}
2018
2019/// Generates an IP packet ID.
2020///
2021/// This is only meaningful for IPv4, see [`IpLayerIpExt`].
2022pub fn gen_ip_packet_id<I: IpLayerIpExt, CC: IpDeviceEgressStateContext<I>>(
2023    core_ctx: &mut CC,
2024) -> I::PacketId {
2025    core_ctx.with_next_packet_id(|state| I::next_packet_id_from_state(state))
2026}
2027
2028/// The stack's IPv6 state.
2029pub struct Ipv6State<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes> {
2030    /// The common inner IP layer state.
2031    pub inner: IpStateInner<Ipv6, StrongDeviceId, BT>,
2032    /// ICMPv6 state.
2033    pub icmp: Icmpv6State<BT>,
2034    /// Stateless address autoconfiguration counters.
2035    pub slaac_counters: SlaacCounters,
2036    /// Secret key used for generating SLAAC temporary addresses.
2037    pub slaac_temp_secret_key: IidSecret,
2038    /// Secret key used for generating SLAAC stable addresses.
2039    ///
2040    /// If `None`, opaque IIDs will not be used to generate stable SLAAC
2041    /// addresses.
2042    pub slaac_stable_secret_key: IidSecret,
2043}
2044
2045impl<StrongDeviceId: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2046    AsRef<IpStateInner<Ipv6, StrongDeviceId, BT>> for Ipv6State<StrongDeviceId, BT>
2047{
2048    fn as_ref(&self) -> &IpStateInner<Ipv6, StrongDeviceId, BT> {
2049        &self.inner
2050    }
2051}
2052
2053impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2054    OrderedLockAccess<IpPacketFragmentCache<I, BT>> for IpStateInner<I, D, BT>
2055{
2056    type Lock = Mutex<IpPacketFragmentCache<I, BT>>;
2057    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2058        OrderedLockRef::new(&self.fragment_cache)
2059    }
2060}
2061
2062impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2063    OrderedLockAccess<PmtuCache<I, BT>> for IpStateInner<I, D, BT>
2064{
2065    type Lock = Mutex<PmtuCache<I, BT>>;
2066    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2067        OrderedLockRef::new(&self.pmtu_cache)
2068    }
2069}
2070
2071impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2072    OrderedLockAccess<RulesTable<I, D, BT>> for IpStateInner<I, D, BT>
2073{
2074    type Lock = RwLock<RulesTable<I, D, BT>>;
2075    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2076        OrderedLockRef::new(&self.rules_table)
2077    }
2078}
2079
2080impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2081    OrderedLockAccess<HashMap<RoutingTableId<I, D, BT>, PrimaryRc<BaseRoutingTableState<I, D, BT>>>>
2082    for IpStateInner<I, D, BT>
2083{
2084    type Lock =
2085        Mutex<HashMap<RoutingTableId<I, D, BT>, PrimaryRc<BaseRoutingTableState<I, D, BT>>>>;
2086    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2087        OrderedLockRef::new(&self.tables)
2088    }
2089}
2090
2091impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpRoutingBindingsTypes>
2092    OrderedLockAccess<RoutingTable<I, D>> for RoutingTableId<I, D, BT>
2093{
2094    type Lock = RwLock<RoutingTable<I, D>>;
2095    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2096        let Self(inner) = self;
2097        OrderedLockRef::new(&inner.routing_table)
2098    }
2099}
2100
2101impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2102    OrderedLockAccess<MulticastForwardingState<I, D, BT>> for IpStateInner<I, D, BT>
2103{
2104    type Lock = RwLock<MulticastForwardingState<I, D, BT>>;
2105    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2106        OrderedLockRef::new(&self.multicast_forwarding)
2107    }
2108}
2109
2110impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2111    OrderedLockAccess<RawIpSocketMap<I, D::Weak, BT>> for IpStateInner<I, D, BT>
2112{
2113    type Lock = RwLock<RawIpSocketMap<I, D::Weak, BT>>;
2114    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2115        OrderedLockRef::new(&self.raw_sockets)
2116    }
2117}
2118
2119impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpLayerBindingsTypes>
2120    OrderedLockAccess<filter::State<I, WeakAddressId<I, BT>, BT>> for IpStateInner<I, D, BT>
2121{
2122    type Lock = RwLock<filter::State<I, WeakAddressId<I, BT>, BT>>;
2123    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
2124        OrderedLockRef::new(&self.filter)
2125    }
2126}
2127
2128/// Marker trait for the bindings types required by the IP layer's inner state.
2129pub trait IpStateBindingsTypes:
2130    PmtuBindingsTypes
2131    + FragmentBindingsTypes
2132    + RawIpSocketsBindingsTypes
2133    + FilterBindingsTypes
2134    + MulticastForwardingBindingsTypes
2135    + IpDeviceStateBindingsTypes
2136    + IpRoutingBindingsTypes
2137{
2138}
2139impl<BT> IpStateBindingsTypes for BT where
2140    BT: PmtuBindingsTypes
2141        + FragmentBindingsTypes
2142        + RawIpSocketsBindingsTypes
2143        + FilterBindingsTypes
2144        + MulticastForwardingBindingsTypes
2145        + IpDeviceStateBindingsTypes
2146        + IpRoutingBindingsTypes
2147{
2148}
2149
2150/// Bindings ID for a routing table.
2151#[derive(Derivative)]
2152#[derivative(Debug(bound = ""))]
2153#[derivative(Clone(bound = "BT::RoutingTableId: Clone"))]
2154pub enum RoutingTableCookie<BT: IpRoutingBindingsTypes> {
2155    /// Main table.
2156    Main,
2157    /// A table added by user (Bindings).
2158    BindingsId(BT::RoutingTableId),
2159}
2160
2161/// State for a routing table.
2162#[derive(Derivative)]
2163#[derivative(Debug(bound = "D: Debug"))]
2164pub struct BaseRoutingTableState<I: Ip, D, BT: IpRoutingBindingsTypes> {
2165    routing_table: RwLock<RoutingTable<I, D>>,
2166    bindings_id: RoutingTableCookie<BT>,
2167}
2168
2169impl<I: Ip, D, BT: IpRoutingBindingsTypes> BaseRoutingTableState<I, D, BT> {
2170    pub(crate) fn with_bindings_id(bindings_id: RoutingTableCookie<BT>) -> Self {
2171        Self { bindings_id, routing_table: Default::default() }
2172    }
2173}
2174
2175/// Identifier to a routing table.
2176#[derive(Derivative)]
2177#[derivative(PartialEq(bound = ""))]
2178#[derivative(Eq(bound = ""))]
2179#[derivative(Hash(bound = ""))]
2180#[derivative(Clone(bound = ""))]
2181pub struct RoutingTableId<I: Ip, D, BT: IpRoutingBindingsTypes>(
2182    StrongRc<BaseRoutingTableState<I, D, BT>>,
2183);
2184
2185impl<I: Ip, D, BT: IpRoutingBindingsTypes> Debug for RoutingTableId<I, D, BT> {
2186    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2187        let Self(rc) = self;
2188        f.debug_tuple("RoutingTableId").field(&I::NAME).field(&rc.bindings_id).finish()
2189    }
2190}
2191
2192impl<I: Ip, D, BT: IpRoutingBindingsTypes> RoutingTableId<I, D, BT> {
2193    /// Creates a new table ID.
2194    pub(crate) fn new(rc: StrongRc<BaseRoutingTableState<I, D, BT>>) -> Self {
2195        Self(rc)
2196    }
2197
2198    /// Provides direct access to the forwarding table.
2199    #[cfg(any(test, feature = "testutils"))]
2200    pub fn table(&self) -> &RwLock<RoutingTable<I, D>> {
2201        let Self(inner) = self;
2202        &inner.routing_table
2203    }
2204
2205    /// Downgrades the strong ID into a weak one.
2206    pub fn downgrade(&self) -> WeakRoutingTableId<I, D, BT>
2207    where
2208        BT::RoutingTableId: Clone,
2209    {
2210        let Self(rc) = self;
2211        WeakRoutingTableId { rc: StrongRc::downgrade(rc), bindings_id: rc.bindings_id.clone() }
2212    }
2213
2214    #[cfg(test)]
2215    fn get_mut(&self) -> impl DerefMut<Target = RoutingTable<I, D>> + '_ {
2216        let Self(rc) = self;
2217        rc.routing_table.write()
2218    }
2219
2220    /// Gets the bindings cookie for this routing table.
2221    pub fn bindings_id(&self) -> &RoutingTableCookie<BT> {
2222        let Self(rc) = self;
2223        &rc.bindings_id
2224    }
2225}
2226
2227/// Weak Identifier to a routing table.
2228#[derive(Derivative)]
2229#[derivative(Clone(bound = "BT::RoutingTableId: Clone"))]
2230#[derivative(PartialEq, Eq, Hash)]
2231pub struct WeakRoutingTableId<I: Ip, D, BT: IpRoutingBindingsTypes> {
2232    rc: WeakRc<BaseRoutingTableState<I, D, BT>>,
2233    #[derivative(PartialEq = "ignore")]
2234    #[derivative(Hash = "ignore")]
2235    bindings_id: RoutingTableCookie<BT>,
2236}
2237
2238impl<I: Ip, D, BT: IpRoutingBindingsTypes> Debug for WeakRoutingTableId<I, D, BT> {
2239    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2240        let Self { bindings_id, .. } = self;
2241        f.debug_tuple("WeakRoutingTableId").field(&I::NAME).field(bindings_id).finish()
2242    }
2243}
2244
2245/// The inner state for the IP layer for IP version `I`.
2246#[derive(GenericOverIp)]
2247#[generic_over_ip(I, Ip)]
2248pub struct IpStateInner<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpStateBindingsTypes> {
2249    rules_table: RwLock<RulesTable<I, D, BT>>,
2250    // TODO(https://fxbug.dev/355059838): Explore the option to let Bindings create the main table.
2251    main_table_id: RoutingTableId<I, D, BT>,
2252    multicast_forwarding: RwLock<MulticastForwardingState<I, D, BT>>,
2253    multicast_forwarding_counters: MulticastForwardingCounters<I>,
2254    fragment_cache: Mutex<IpPacketFragmentCache<I, BT>>,
2255    pmtu_cache: Mutex<PmtuCache<I, BT>>,
2256    counters: IpCounters<I>,
2257    raw_sockets: RwLock<RawIpSocketMap<I, D::Weak, BT>>,
2258    raw_socket_counters: RawIpSocketCounters<I>,
2259    filter: RwLock<filter::State<I, WeakAddressId<I, BT>, BT>>,
2260    // Make sure the primary IDs are dropped last. Also note that the following hash map also stores
2261    // the primary ID to the main table, and if the user (Bindings) attempts to remove the main
2262    // table without dropping `main_table_id` first, it will panic. This serves as an assertion
2263    // that the main table cannot be removed and Bindings must never attempt to remove the main
2264    // routing table.
2265    tables: Mutex<HashMap<RoutingTableId<I, D, BT>, PrimaryRc<BaseRoutingTableState<I, D, BT>>>>,
2266    igmp_counters: IgmpCounters,
2267    mld_counters: MldCounters,
2268}
2269
2270impl<I: IpLayerIpExt, D: StrongDeviceIdentifier, BT: IpStateBindingsTypes> IpStateInner<I, D, BT> {
2271    /// Gets the IP counters.
2272    pub fn counters(&self) -> &IpCounters<I> {
2273        &self.counters
2274    }
2275
2276    /// Gets the multicast forwarding counters.
2277    pub fn multicast_forwarding_counters(&self) -> &MulticastForwardingCounters<I> {
2278        &self.multicast_forwarding_counters
2279    }
2280
2281    /// Gets the aggregate raw IP socket counters.
2282    pub fn raw_ip_socket_counters(&self) -> &RawIpSocketCounters<I> {
2283        &self.raw_socket_counters
2284    }
2285
2286    /// Gets the main table ID.
2287    pub fn main_table_id(&self) -> &RoutingTableId<I, D, BT> {
2288        &self.main_table_id
2289    }
2290
2291    /// Provides direct access to the path MTU cache.
2292    #[cfg(any(test, feature = "testutils"))]
2293    pub fn pmtu_cache(&self) -> &Mutex<PmtuCache<I, BT>> {
2294        &self.pmtu_cache
2295    }
2296
2297    /// Provides direct access to the filtering state.
2298    #[cfg(any(test, feature = "testutils"))]
2299    pub fn filter(&self) -> &RwLock<filter::State<I, WeakAddressId<I, BT>, BT>> {
2300        &self.filter
2301    }
2302
2303    /// Gets the stack-wide IGMP counters.
2304    pub fn igmp_counters(&self) -> &IgmpCounters {
2305        &self.igmp_counters
2306    }
2307
2308    /// Gets the stack-wide MLD counters.
2309    pub fn mld_counters(&self) -> &MldCounters {
2310        &self.mld_counters
2311    }
2312}
2313
2314impl<
2315    I: IpLayerIpExt,
2316    D: StrongDeviceIdentifier,
2317    BC: TimerContext + RngContext + IpStateBindingsTypes + IpRoutingBindingsTypes,
2318> IpStateInner<I, D, BC>
2319{
2320    /// Creates a new inner IP layer state.
2321    fn new<CC: CoreTimerContext<IpLayerTimerId, BC>>(bindings_ctx: &mut BC) -> Self {
2322        let main_table: PrimaryRc<BaseRoutingTableState<I, D, BC>> =
2323            PrimaryRc::new(BaseRoutingTableState::with_bindings_id(RoutingTableCookie::Main));
2324        let main_table_id = RoutingTableId(PrimaryRc::clone_strong(&main_table));
2325        Self {
2326            rules_table: RwLock::new(RulesTable::new(main_table_id.clone())),
2327            tables: Mutex::new(HashMap::from_iter(core::iter::once((
2328                main_table_id.clone(),
2329                main_table,
2330            )))),
2331            main_table_id,
2332            multicast_forwarding: Default::default(),
2333            multicast_forwarding_counters: Default::default(),
2334            fragment_cache: Mutex::new(
2335                IpPacketFragmentCache::new::<NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx),
2336            ),
2337            pmtu_cache: Mutex::new(PmtuCache::new::<NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx)),
2338            counters: Default::default(),
2339            raw_sockets: Default::default(),
2340            raw_socket_counters: Default::default(),
2341            filter: RwLock::new(filter::State::new::<NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx)),
2342            igmp_counters: Default::default(),
2343            mld_counters: Default::default(),
2344        }
2345    }
2346}
2347
2348/// The identifier for timer events in the IP layer.
2349#[derive(Debug, Clone, Eq, PartialEq, Hash, GenericOverIp)]
2350#[generic_over_ip()]
2351pub enum IpLayerTimerId {
2352    /// A timer event for IPv4 packet reassembly timers.
2353    ReassemblyTimeoutv4(FragmentTimerId<Ipv4>),
2354    /// A timer event for IPv6 packet reassembly timers.
2355    ReassemblyTimeoutv6(FragmentTimerId<Ipv6>),
2356    /// A timer event for IPv4 path MTU discovery.
2357    PmtuTimeoutv4(PmtuTimerId<Ipv4>),
2358    /// A timer event for IPv6 path MTU discovery.
2359    PmtuTimeoutv6(PmtuTimerId<Ipv6>),
2360    /// A timer event for IPv4 filtering timers.
2361    FilterTimerv4(FilterTimerId<Ipv4>),
2362    /// A timer event for IPv6 filtering timers.
2363    FilterTimerv6(FilterTimerId<Ipv6>),
2364    /// A timer event for IPv4 Multicast forwarding timers.
2365    MulticastForwardingTimerv4(MulticastForwardingTimerId<Ipv4>),
2366    /// A timer event for IPv6 Multicast forwarding timers.
2367    MulticastForwardingTimerv6(MulticastForwardingTimerId<Ipv6>),
2368}
2369
2370impl<I: Ip> From<FragmentTimerId<I>> for IpLayerTimerId {
2371    fn from(timer: FragmentTimerId<I>) -> IpLayerTimerId {
2372        I::map_ip(timer, IpLayerTimerId::ReassemblyTimeoutv4, IpLayerTimerId::ReassemblyTimeoutv6)
2373    }
2374}
2375
2376impl<I: Ip> From<PmtuTimerId<I>> for IpLayerTimerId {
2377    fn from(timer: PmtuTimerId<I>) -> IpLayerTimerId {
2378        I::map_ip(timer, IpLayerTimerId::PmtuTimeoutv4, IpLayerTimerId::PmtuTimeoutv6)
2379    }
2380}
2381
2382impl<I: Ip> From<FilterTimerId<I>> for IpLayerTimerId {
2383    fn from(timer: FilterTimerId<I>) -> IpLayerTimerId {
2384        I::map_ip(timer, IpLayerTimerId::FilterTimerv4, IpLayerTimerId::FilterTimerv6)
2385    }
2386}
2387
2388impl<I: Ip> From<MulticastForwardingTimerId<I>> for IpLayerTimerId {
2389    fn from(timer: MulticastForwardingTimerId<I>) -> IpLayerTimerId {
2390        I::map_ip(
2391            timer,
2392            IpLayerTimerId::MulticastForwardingTimerv4,
2393            IpLayerTimerId::MulticastForwardingTimerv6,
2394        )
2395    }
2396}
2397
2398impl<CC, BC> HandleableTimer<CC, BC> for IpLayerTimerId
2399where
2400    CC: TimerHandler<BC, FragmentTimerId<Ipv4>>
2401        + TimerHandler<BC, FragmentTimerId<Ipv6>>
2402        + TimerHandler<BC, PmtuTimerId<Ipv4>>
2403        + TimerHandler<BC, PmtuTimerId<Ipv6>>
2404        + TimerHandler<BC, FilterTimerId<Ipv4>>
2405        + TimerHandler<BC, FilterTimerId<Ipv6>>
2406        + TimerHandler<BC, MulticastForwardingTimerId<Ipv4>>
2407        + TimerHandler<BC, MulticastForwardingTimerId<Ipv6>>,
2408    BC: TimerBindingsTypes,
2409{
2410    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
2411        match self {
2412            IpLayerTimerId::ReassemblyTimeoutv4(id) => {
2413                core_ctx.handle_timer(bindings_ctx, id, timer)
2414            }
2415            IpLayerTimerId::ReassemblyTimeoutv6(id) => {
2416                core_ctx.handle_timer(bindings_ctx, id, timer)
2417            }
2418            IpLayerTimerId::PmtuTimeoutv4(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
2419            IpLayerTimerId::PmtuTimeoutv6(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
2420            IpLayerTimerId::FilterTimerv4(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
2421            IpLayerTimerId::FilterTimerv6(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
2422            IpLayerTimerId::MulticastForwardingTimerv4(id) => {
2423                core_ctx.handle_timer(bindings_ctx, id, timer)
2424            }
2425            IpLayerTimerId::MulticastForwardingTimerv6(id) => {
2426                core_ctx.handle_timer(bindings_ctx, id, timer)
2427            }
2428        }
2429    }
2430}
2431
2432/// An ICMP error, and the metadata required to send it.
2433///
2434/// This allows the sending of the ICMP error to be decoupled from the
2435/// generation of the error, which is advantageous because sending the error
2436/// requires the underlying packet buffer, which cannot be "moved" in certain
2437/// contexts.
2438pub(crate) struct IcmpErrorSender<'a, I: IcmpHandlerIpExt, D> {
2439    /// The ICMP error that should be sent.
2440    err: I::IcmpError,
2441    /// The original source IP address of the packet (before the local-ingress
2442    /// hook evaluation).
2443    src_ip: SocketIpAddr<I::Addr>,
2444    /// The original destination IP address of the packet (before the
2445    /// local-ingress hook evaluation).
2446    dst_ip: SocketIpAddr<I::Addr>,
2447    /// The frame destination of the packet.
2448    frame_dst: Option<LocalFrameDestination>,
2449    /// The device out which to send the error.
2450    device: &'a D,
2451    /// The metadata from the packet, allowing the packet's backing buffer to be
2452    /// returned to it's pre-IP-parse state with [`GrowBuffer::undo_parse`].
2453    meta: ParseMetadata,
2454    /// The marks used to send the ICMP error.
2455    marks: Marks,
2456    /// The protocol of the original packet.
2457    proto: I::Proto,
2458}
2459
2460impl<'a, I: IcmpHandlerIpExt, D> IcmpErrorSender<'a, I, D> {
2461    pub fn new<CC, B>(
2462        core_ctx: &mut CC,
2463        err: I::IcmpError,
2464        packet: &I::Packet<B>,
2465        frame_dst: Option<LocalFrameDestination>,
2466        device: &'a D,
2467        marks: Marks,
2468    ) -> Option<Self>
2469    where
2470        I: IpCountersIpExt,
2471        CC: ResourceCounterContext<D, IpCounters<I>>,
2472        B: SplitByteSlice,
2473    {
2474        let Some(src_ip) = SocketIpAddr::new(packet.src_ip()) else {
2475            core_ctx.increment_both(device, |c| &c.unspecified_source);
2476            return None;
2477        };
2478        let Some(dst_ip) = SocketIpAddr::new(packet.dst_ip()) else {
2479            return None;
2480        };
2481
2482        // In IPv4, don't respond to non-initial fragments.
2483        let is_ipv4_fragment = I::map_ip_in(
2484            packet,
2485            |p| {
2486                packet_formats::ipv4::Ipv4Header::fragment_type(p)
2487                    == Ipv4FragmentType::NonInitialFragment
2488            },
2489            |_| false,
2490        );
2491        if is_ipv4_fragment {
2492            return None;
2493        }
2494
2495        let meta = packet.parse_metadata();
2496        let proto = packet.proto();
2497        Some(Self { err, src_ip, dst_ip, frame_dst, device, meta, marks, proto })
2498    }
2499
2500    /// Generate an send an appropriate ICMP error in response to this error.
2501    ///
2502    /// The provided `body` must be the original buffer from which the IP
2503    /// packet responsible for this error was parsed. It is expected to be in a
2504    /// state that allows undoing the IP packet parse (e.g. unmodified after the
2505    /// IP packet was parsed).
2506    pub fn send<B, BC, CC>(self, core_ctx: &mut CC, bindings_ctx: &mut BC, mut body: B)
2507    where
2508        B: BufferMut,
2509        CC: IcmpErrorHandler<I, BC, DeviceId = D>,
2510    {
2511        let IcmpErrorSender { err, src_ip, dst_ip, frame_dst, device, meta, marks, proto } = self;
2512        let header_len = meta.header_len();
2513
2514        // Undo the parsing of the IP Packet, moving the buffer's cursor so that
2515        // it points at the start of the IP header. This way, the sent ICMP
2516        // error will contain the entire original IP packet.
2517        body.undo_parse(meta);
2518
2519        core_ctx.send_icmp_error_message(
2520            bindings_ctx,
2521            Some(device),
2522            frame_dst,
2523            src_ip,
2524            dst_ip,
2525            body,
2526            err,
2527            header_len,
2528            proto,
2529            &marks,
2530        );
2531    }
2532}
2533
2534// Early demux results may be invalidated by SNAT in the LOCAL_INGRESS hook.
2535// This struct is used to check if the early demux result is still valid.
2536//
2537// TODO(https://fxbug.dev/476507679): Add tests to ensure this works properly
2538// once SNAT is fully implemented.
2539#[derive(PartialEq, Eq)]
2540struct EarlyDemuxResult<I: Ip, S> {
2541    socket: S,
2542    src_addr: I::Addr,
2543    src_port: Option<u16>,
2544}
2545
2546impl<I: FilterIpExt, S> EarlyDemuxResult<I, S> {
2547    fn new<P: IpPacket<I>>(socket: S, packet: &P) -> Self {
2548        let src_port =
2549            packet.maybe_transport_packet().transport_packet_data().map(|t| t.src_port());
2550        Self { socket, src_addr: packet.src_addr(), src_port }
2551    }
2552
2553    // Returns the socket if it's still the right socket to handle the packet.
2554    fn take_socket<P: IpPacket<I>>(self, packet: &P) -> Option<S> {
2555        let src_port =
2556            packet.maybe_transport_packet().transport_packet_data().map(|t| t.src_port());
2557        (self.src_addr == packet.src_addr() && self.src_port == src_port).then_some(self.socket)
2558    }
2559
2560    fn update_packet_metadata<CC, BC>(
2561        &self,
2562        core_ctx: &mut CC,
2563        packet_metadata: &mut IpLayerPacketMetadata<I, CC::WeakAddressId, BC>,
2564    ) where
2565        I: IpLayerIpExt,
2566        S: SocketMetadata<CC>,
2567        BC: IpLayerBindingsContext<I, CC::DeviceId>,
2568        CC: IpLayerIngressContext<I, BC>,
2569    {
2570        packet_metadata.socket_info = Some(self.socket.socket_info(core_ctx));
2571        packet_metadata.marks =
2572            BC::update_ingress_marks(packet_metadata.marks, &self.socket.marks(core_ctx));
2573    }
2574}
2575
2576pub(crate) fn reject_type_to_icmpv4_error(reject_type: RejectType) -> Option<Icmpv4Error> {
2577    let error = match reject_type {
2578        RejectType::NetUnreachable => Icmpv4Error::NetUnreachable,
2579        RejectType::ProtoUnreachable => Icmpv4Error::ProtocolUnreachable,
2580        RejectType::PortUnreachable => Icmpv4Error::PortUnreachable,
2581        RejectType::HostUnreachable => Icmpv4Error::HostUnreachable,
2582        RejectType::RoutePolicyFail => Icmpv4Error::NetworkProhibited,
2583        RejectType::RejectRoute => Icmpv4Error::HostProhibited,
2584        RejectType::AdminProhibited => Icmpv4Error::AdminProhibited,
2585        // TODO(https://fxbug.dev/488116504): Implement RejectType::TcpReset.
2586        RejectType::TcpReset => return None,
2587    };
2588    Some(error)
2589}
2590
2591pub(crate) fn reject_type_to_icmpv6_error(reject_type: RejectType) -> Option<Icmpv6Error> {
2592    let error = match reject_type {
2593        RejectType::NetUnreachable => Icmpv6Error::NetUnreachable,
2594        RejectType::PortUnreachable => Icmpv6Error::PortUnreachable,
2595        RejectType::HostUnreachable => Icmpv6Error::AddressUnreachable,
2596        RejectType::AdminProhibited => Icmpv6Error::AdminProhibited,
2597        RejectType::RoutePolicyFail => Icmpv6Error::SourceAddressPolicyFailed,
2598        RejectType::RejectRoute => Icmpv6Error::RejectRoute,
2599        // TODO(https://fxbug.dev/488116504): Implement ProtoUnreachable and TcpReset.
2600        RejectType::TcpReset | RejectType::ProtoUnreachable => return None,
2601    };
2602    Some(error)
2603}
2604// TODO(joshlf): Once we support multiple extension headers in IPv6, we will
2605// need to verify that the callers of this function are still sound. In
2606// particular, they may accidentally pass a parse_metadata argument which
2607// corresponds to a single extension header rather than all of the IPv6 headers.
2608
2609/// Dispatch a received IPv4 packet to the appropriate protocol.
2610///
2611/// `device` is the device the packet was received on. `parse_metadata` is the
2612/// parse metadata associated with parsing the IP headers. It is used to undo
2613/// that parsing. Both `device` and `parse_metadata` are required in order to
2614/// send ICMP messages in response to unrecognized protocols or ports. If either
2615/// of `device` or `parse_metadata` is `None`, the caller promises that the
2616/// protocol and port are recognized.
2617///
2618/// # Panics
2619///
2620/// `dispatch_receive_ipv4_packet` panics if the protocol is unrecognized and
2621/// `parse_metadata` is `None`. If an IGMP message is received but it is not
2622/// coming from a device, i.e., `device` given is `None`,
2623/// `dispatch_receive_ip_packet` will also panic.
2624fn dispatch_receive_ipv4_packet<
2625    'a,
2626    'b,
2627    BC: IpLayerBindingsContext<Ipv4, CC::DeviceId>,
2628    CC: IpLayerIngressContext<Ipv4, BC>,
2629>(
2630    core_ctx: &'a mut CC,
2631    bindings_ctx: &'a mut BC,
2632    device: &'b CC::DeviceId,
2633    frame_dst: Option<LocalFrameDestination>,
2634    mut packet: Ipv4Packet<&'a mut [u8]>,
2635    mut packet_metadata: IpLayerPacketMetadata<Ipv4, CC::WeakAddressId, BC>,
2636    receive_meta: ReceiveIpPacketMeta<Ipv4>,
2637) -> Result<(), IcmpErrorSender<'b, Ipv4, CC::DeviceId>> {
2638    core_ctx.increment_both(device, |c| &c.dispatch_receive_ip_packet);
2639
2640    // Skip early demux if the packet was redirected to a TPROXY.
2641    // TODO(https://fxbug.dev/475851987): Handle TPROXY in early_demux.
2642    let early_demux_result = receive_meta
2643        .transparent_override
2644        .is_none()
2645        .then(|| {
2646            core_ctx.early_demux(
2647                device,
2648                frame_dst,
2649                packet.src_ip(),
2650                packet.dst_ip(),
2651                packet.proto(),
2652                packet.body(),
2653            )
2654        })
2655        .flatten()
2656        .map(|socket| {
2657            let early_demux_result = EarlyDemuxResult::new(socket, &packet);
2658            early_demux_result.update_packet_metadata(core_ctx, &mut packet_metadata);
2659            early_demux_result
2660        });
2661
2662    let filter_verdict = core_ctx.filter_handler().local_ingress_hook(
2663        bindings_ctx,
2664        &mut packet,
2665        device,
2666        &mut packet_metadata,
2667    );
2668
2669    let marks = packet_metadata.marks;
2670    packet_metadata.acknowledge_drop();
2671
2672    match filter_verdict {
2673        filter::Verdict::Stop(filter::DropOrReject::Drop) => {
2674            return Ok(());
2675        }
2676        filter::Verdict::Stop(filter::DropOrReject::Reject(reject_type)) => {
2677            return match reject_type_to_icmpv4_error(reject_type) {
2678                Some(icmp_error) => {
2679                    match IcmpErrorSender::new(
2680                        core_ctx, icmp_error, &packet, frame_dst, device, marks,
2681                    ) {
2682                        Some(icmp_sender) => Err(icmp_sender),
2683                        None => Ok(()),
2684                    }
2685                }
2686                None => {
2687                    debug!("Unsupported reject type: {:?}", reject_type);
2688                    return Ok(());
2689                }
2690            };
2691        }
2692        filter::Verdict::Proceed(filter::Accept) => (),
2693    };
2694
2695    // These invariants are validated by the caller of this function, but it's
2696    // possible for the LOCAL_INGRESS hook to rewrite the packet, so we have to
2697    // check them again.
2698    let Some(src_ip) = packet.src_ipv4() else {
2699        debug!(
2700            "dispatch_receive_ipv4_packet: received packet from invalid source {} after the \
2701            LOCAL_INGRESS hook; dropping",
2702            packet.src_ip()
2703        );
2704        core_ctx.increment_both(device, |c| &c.invalid_source);
2705        return Ok(());
2706    };
2707    let Some(dst_ip) = SpecifiedAddr::new(packet.dst_ip()) else {
2708        core_ctx.increment_both(device, |c| &c.unspecified_destination);
2709        debug!(
2710            "dispatch_receive_ipv4_packet: Received packet with unspecified destination IP address \
2711            after the LOCAL_INGRESS hook; dropping"
2712        );
2713        return Ok(());
2714    };
2715
2716    core_ctx.deliver_packet_to_raw_ip_sockets(bindings_ctx, &packet, &device);
2717
2718    // Check if the early demux result is still valid.
2719    let early_demux_socket = early_demux_result.and_then(|result| result.take_socket(&packet));
2720
2721    let proto = packet.proto();
2722    let (prefix, options, body) = packet.parts_with_body_mut();
2723    let buffer = Buf::new(body, ..);
2724    let header_info = Ipv4HeaderInfo { prefix, options: options.as_ref() };
2725    let mut receive_info = LocalDeliveryPacketInfo { meta: receive_meta, header_info, marks };
2726
2727    core_ctx
2728        .dispatch_receive_ip_packet(
2729            bindings_ctx,
2730            device,
2731            src_ip,
2732            dst_ip,
2733            proto,
2734            buffer,
2735            &mut receive_info,
2736            early_demux_socket,
2737        )
2738        .or_else(|icmp_error| {
2739            match IcmpErrorSender::new(core_ctx, icmp_error, &packet, frame_dst, device, marks) {
2740                Some(icmp_sender) => Err(icmp_sender),
2741                None => Ok(()),
2742            }
2743        })
2744}
2745
2746/// Dispatch a received IPv6 packet to the appropriate protocol.
2747///
2748/// `dispatch_receive_ipv6_packet` has the same semantics as
2749/// `dispatch_receive_ipv4_packet`, but for IPv6.
2750fn dispatch_receive_ipv6_packet<
2751    'a,
2752    'b,
2753    BC: IpLayerBindingsContext<Ipv6, CC::DeviceId>,
2754    CC: IpLayerIngressContext<Ipv6, BC>,
2755>(
2756    core_ctx: &'a mut CC,
2757    bindings_ctx: &'a mut BC,
2758    device: &'b CC::DeviceId,
2759    frame_dst: Option<LocalFrameDestination>,
2760    mut packet: Ipv6Packet<&'a mut [u8]>,
2761    mut packet_metadata: IpLayerPacketMetadata<Ipv6, CC::WeakAddressId, BC>,
2762    meta: ReceiveIpPacketMeta<Ipv6>,
2763) -> Result<(), IcmpErrorSender<'b, Ipv6, CC::DeviceId>> {
2764    // TODO(https://fxbug.dev/42095067): Once we support multiple extension
2765    // headers in IPv6, we will need to verify that the callers of this
2766    // function are still sound. In particular, they may accidentally pass a
2767    // parse_metadata argument which corresponds to a single extension
2768    // header rather than all of the IPv6 headers.
2769
2770    core_ctx.increment_both(device, |c| &c.dispatch_receive_ip_packet);
2771
2772    // Skip early demux if the packet was redirected to a TPROXY.
2773    // TODO(https://fxbug.dev/475851987): Handle TPROXY in early_demux.
2774    let early_demux_result = meta
2775        .transparent_override
2776        .is_none()
2777        .then(|| {
2778            core_ctx.early_demux(
2779                device,
2780                frame_dst,
2781                packet.src_ip(),
2782                packet.dst_ip(),
2783                packet.proto(),
2784                packet.body(),
2785            )
2786        })
2787        .flatten()
2788        .map(|socket| {
2789            let early_demux_result = EarlyDemuxResult::new(socket, &packet);
2790            early_demux_result.update_packet_metadata(core_ctx, &mut packet_metadata);
2791            early_demux_result
2792        });
2793
2794    let filter_verdict = core_ctx.filter_handler().local_ingress_hook(
2795        bindings_ctx,
2796        &mut packet,
2797        device,
2798        &mut packet_metadata,
2799    );
2800
2801    let marks = packet_metadata.marks;
2802    packet_metadata.acknowledge_drop();
2803
2804    match filter_verdict {
2805        filter::Verdict::Stop(filter::DropOrReject::Drop) => {
2806            return Ok(());
2807        }
2808        filter::Verdict::Stop(filter::DropOrReject::Reject(reject_type)) => {
2809            return match reject_type_to_icmpv6_error(reject_type) {
2810                Some(icmp_error) => {
2811                    match IcmpErrorSender::new(
2812                        core_ctx, icmp_error, &packet, frame_dst, device, marks,
2813                    ) {
2814                        Some(icmp_sender) => Err(icmp_sender),
2815                        None => Ok(()),
2816                    }
2817                }
2818                None => {
2819                    debug!("Unsupported reject type: {:?}", reject_type);
2820                    return Ok(());
2821                }
2822            };
2823        }
2824        filter::Verdict::Proceed(filter::Accept) => {}
2825    }
2826
2827    // These invariants are validated by the caller of this function, but it's
2828    // possible for the LOCAL_INGRESS hook to rewrite the packet, so we have to
2829    // check them again.
2830    let Some(src_ip) = packet.src_ipv6() else {
2831        debug!(
2832            "dispatch_receive_ipv6_packet: received packet from invalid source {} after the \
2833            LOCAL_INGRESS hook; dropping",
2834            packet.src_ip()
2835        );
2836
2837        core_ctx.increment_both(device, |c| &c.invalid_source);
2838        return Ok(());
2839    };
2840    let Some(dst_ip) = SpecifiedAddr::new(packet.dst_ip()) else {
2841        core_ctx.increment_both(device, |c| &c.unspecified_destination);
2842        debug!(
2843            "dispatch_receive_ipv6_packet: Received packet with unspecified destination IP address \
2844            after the LOCAL_INGRESS hook; dropping"
2845        );
2846        return Ok(());
2847    };
2848
2849    core_ctx.deliver_packet_to_raw_ip_sockets(bindings_ctx, &packet, &device);
2850
2851    // Check if the early demux result is still valid.
2852    let early_demux_socket = early_demux_result.and_then(|result| result.take_socket(&packet));
2853
2854    let proto = packet.proto();
2855    let (fixed, extension, body) = packet.parts_with_body_mut();
2856    let buffer = Buf::new(body, ..);
2857    let header_info = Ipv6HeaderInfo { fixed, extension };
2858    let mut receive_info = LocalDeliveryPacketInfo { meta, header_info, marks };
2859
2860    core_ctx
2861        .dispatch_receive_ip_packet(
2862            bindings_ctx,
2863            device,
2864            src_ip,
2865            dst_ip,
2866            proto,
2867            buffer,
2868            &mut receive_info,
2869            early_demux_socket,
2870        )
2871        .or_else(|icmp_error| {
2872            let marks = receive_info.marks;
2873            match IcmpErrorSender::new(core_ctx, icmp_error, &packet, frame_dst, device, marks) {
2874                Some(icmp_sender) => Err(icmp_sender),
2875                None => Ok(()),
2876            }
2877        })
2878}
2879
2880/// The metadata required to forward an IP Packet.
2881///
2882/// This allows the forwarding of the packet to be decoupled from the
2883/// determination of how to forward. This is advantageous because forwarding
2884/// requires the underlying packet buffer, which cannot be "moved" in certain
2885/// contexts.
2886pub(crate) struct IpPacketForwarder<
2887    'a,
2888    I: IpLayerIpExt,
2889    D,
2890    A,
2891    BT: FilterBindingsTypes + TxMetadataBindingsTypes,
2892> {
2893    inbound_device: &'a D,
2894    outbound_device: &'a D,
2895    packet_meta: IpLayerPacketMetadata<I, A, BT>,
2896    src_ip: I::RecvSrcAddr,
2897    dst_ip: SpecifiedAddr<I::Addr>,
2898    destination: IpPacketDestination<I, &'a D>,
2899    proto: I::Proto,
2900    parse_meta: ParseMetadata,
2901    frame_dst: Option<LocalFrameDestination>,
2902}
2903
2904impl<'a, I, D, A, BC> IpPacketForwarder<'a, I, D, A, BC>
2905where
2906    I: IpLayerIpExt,
2907    BC: IpLayerBindingsContext<I, D>,
2908{
2909    // Forward the provided buffer as specified by this [`IpPacketForwarder`].
2910    fn forward_with_buffer<CC, B>(
2911        self,
2912        core_ctx: &mut CC,
2913        bindings_ctx: &mut BC,
2914        buffer: B,
2915        max_fragment_len: Option<usize>,
2916    ) where
2917        B: BufferMut,
2918        CC: IpLayerForwardingContext<I, BC, DeviceId = D, WeakAddressId = A>,
2919    {
2920        let Self {
2921            inbound_device,
2922            outbound_device,
2923            packet_meta,
2924            src_ip,
2925            dst_ip,
2926            destination,
2927            proto,
2928            parse_meta,
2929            frame_dst,
2930        } = self;
2931
2932        let outbound_mtu = core_ctx.get_mtu(outbound_device);
2933        let marks = packet_meta.marks;
2934
2935        let send_icmp_packet_too_big = |core_ctx: &mut CC, bindings_ctx: &mut BC, buffer: B| {
2936            debug!("failed to forward {} packet: MTU exceeded", I::NAME);
2937            core_ctx.increment_both(outbound_device, |c| &c.mtu_exceeded);
2938            // NB: Ipv6 sends a PacketTooBig error. Ipv4 sends nothing.
2939            let Some(err) = I::IcmpError::mtu_exceeded(outbound_mtu) else {
2940                return;
2941            };
2942            // NB: Only send an ICMP error if the sender's src
2943            // is specified.
2944            let Some(src_ip) = I::received_source_as_icmp_source(src_ip) else {
2945                return;
2946            };
2947
2948            let Some(dst_ip) = SocketIpAddr::new(dst_ip.get()) else {
2949                return;
2950            };
2951
2952            // TODO(https://fxbug.dev/362489447): Increment the TTL since we
2953            // just decremented it. The fact that we don't do this is
2954            // technically a violation of the ICMP spec (we're not
2955            // encapsulating the original packet that caused the
2956            // issue, but a slightly modified version of it), but
2957            // it's not that big of a deal because it won't affect
2958            // the sender's ability to figure out the minimum path
2959            // MTU. This may break other logic, though, so we should
2960            // still fix it eventually.
2961            core_ctx.send_icmp_error_message(
2962                bindings_ctx,
2963                Some(inbound_device),
2964                frame_dst,
2965                src_ip,
2966                dst_ip,
2967                buffer,
2968                err,
2969                parse_meta.header_len(),
2970                proto,
2971                &marks,
2972            );
2973        };
2974
2975        // If the packet was reassembled on ingress, we should refragment at
2976        // the original MTU.
2977        //
2978        // For IPv6, if the maximum fragment was larger than the outbound
2979        // interface's MTU, short circuit and send a `PacketTooBig` ICMP error.
2980        let max_fragment_len = max_fragment_len
2981            .map(|l| Mtu::new(u32::try_from(l).expect("fragment size must fit in u32")));
2982        if I::VERSION == IpVersion::V6
2983            && max_fragment_len.is_some_and(|max_fragment_len| max_fragment_len > outbound_mtu)
2984        {
2985            packet_meta.acknowledge_drop();
2986            send_icmp_packet_too_big(core_ctx, bindings_ctx, buffer);
2987            return;
2988        }
2989        let (was_reassembled, limit_mtu) = match max_fragment_len {
2990            None => (false, Mtu::no_limit()),
2991            Some(max_fragment_len) => (true, max_fragment_len),
2992        };
2993
2994        let packet = ForwardedPacket::new(
2995            src_ip.get(),
2996            dst_ip.get(),
2997            proto,
2998            parse_meta,
2999            buffer,
3000            was_reassembled,
3001        );
3002
3003        trace!("forward_with_buffer: forwarding {} packet", I::NAME);
3004
3005        match send_ip_frame(
3006            core_ctx,
3007            bindings_ctx,
3008            outbound_device,
3009            destination,
3010            packet,
3011            packet_meta,
3012            limit_mtu,
3013        ) {
3014            Ok(()) => (),
3015            Err(IpSendFrameError { serializer, error }) => {
3016                match error {
3017                    IpSendFrameErrorReason::Device(
3018                        SendFrameErrorReason::SizeConstraintsViolation,
3019                    ) => {
3020                        send_icmp_packet_too_big(core_ctx, bindings_ctx, serializer.into_buffer());
3021                    }
3022                    IpSendFrameErrorReason::Device(SendFrameErrorReason::QueueFull)
3023                    | IpSendFrameErrorReason::Device(SendFrameErrorReason::Alloc)
3024                    | IpSendFrameErrorReason::Device(
3025                        SendFrameErrorReason::AddressResolutionFailed,
3026                    )
3027                    | IpSendFrameErrorReason::IllegalLoopbackAddress => (),
3028                }
3029                debug!("failed to forward {} packet: {error:?}", I::NAME);
3030            }
3031        }
3032    }
3033}
3034
3035/// The action to take for a packet that was a candidate for forwarding.
3036pub(crate) enum ForwardingAction<
3037    'a,
3038    I: IpLayerIpExt,
3039    D,
3040    A,
3041    BT: FilterBindingsTypes + TxMetadataBindingsTypes,
3042> {
3043    /// Drop the packet without forwarding it or generating an ICMP error.
3044    SilentlyDrop,
3045    /// Forward the packet, as specified by the [`IpPacketForwarder`].
3046    Forward(IpPacketForwarder<'a, I, D, A, BT>),
3047    /// Drop the packet without forwarding, and generate an ICMP error as
3048    /// specified by the [`IcmpErrorSender`].
3049    DropWithIcmpError(IcmpErrorSender<'a, I, D>),
3050}
3051
3052impl<'a, I, D, A, BC> ForwardingAction<'a, I, D, A, BC>
3053where
3054    I: IpLayerIpExt,
3055    BC: IpLayerBindingsContext<I, D>,
3056{
3057    /// Perform the action prescribed by self, with the provided packet buffer.
3058    pub(crate) fn perform_action_with_buffer<CC, B>(
3059        self,
3060        core_ctx: &mut CC,
3061        bindings_ctx: &mut BC,
3062        buffer: B,
3063        max_fragment_len: Option<usize>,
3064    ) where
3065        B: BufferMut,
3066        CC: IpLayerForwardingContext<I, BC, DeviceId = D, WeakAddressId = A>,
3067    {
3068        match self {
3069            ForwardingAction::SilentlyDrop => {}
3070            ForwardingAction::Forward(forwarder) => {
3071                forwarder.forward_with_buffer(core_ctx, bindings_ctx, buffer, max_fragment_len)
3072            }
3073            ForwardingAction::DropWithIcmpError(icmp_sender) => {
3074                icmp_sender.send(core_ctx, bindings_ctx, buffer)
3075            }
3076        }
3077    }
3078}
3079
3080/// Determine which [`ForwardingAction`] should be taken for an IP packet.
3081pub(crate) fn determine_ip_packet_forwarding_action<'a, 'b, I, BC, CC>(
3082    core_ctx: &'a mut CC,
3083    mut packet: I::Packet<&'a mut [u8]>,
3084    mut packet_meta: IpLayerPacketMetadata<I, CC::WeakAddressId, BC>,
3085    minimum_ttl: Option<NonZeroU8>,
3086    inbound_device: &'b CC::DeviceId,
3087    outbound_device: &'b CC::DeviceId,
3088    destination: IpPacketDestination<I, &'b CC::DeviceId>,
3089    frame_dst: Option<LocalFrameDestination>,
3090    src_ip: I::RecvSrcAddr,
3091    dst_ip: SpecifiedAddr<I::Addr>,
3092) -> ForwardingAction<'b, I, CC::DeviceId, CC::WeakAddressId, BC>
3093where
3094    I: IpLayerIpExt,
3095    BC: IpLayerBindingsContext<I, CC::DeviceId>,
3096    CC: IpLayerForwardingContext<I, BC>,
3097{
3098    // When forwarding, if a datagram's TTL is one or zero, discard it, as
3099    // decrementing the TTL would put it below the allowed minimum value.
3100    // For IPv4, see "TTL" section, https://tools.ietf.org/html/rfc791#page-14.
3101    // For IPv6, see "Hop Limit" section, https://datatracker.ietf.org/doc/html/rfc2460#page-5.
3102    const DEFAULT_MIN_TTL: u8 = 1;
3103    let minimum_ttl = minimum_ttl.map(NonZeroU8::get).unwrap_or(DEFAULT_MIN_TTL);
3104    let ttl = packet.ttl();
3105    if ttl <= minimum_ttl {
3106        debug!(
3107            "{} packet not forwarded due to inadequate TTL: got={ttl} minimum={minimum_ttl}",
3108            I::NAME
3109        );
3110        // As per RFC 792's specification of the Time Exceeded Message:
3111        //     If the gateway processing a datagram finds the time to live
3112        //     field is zero it must discard the datagram. The gateway may
3113        //     also notify the source host via the time exceeded message.
3114        // And RFC 4443 section 3.3:
3115        //    If a router receives a packet with a Hop Limit of zero, or if
3116        //    a router decrements a packet's Hop Limit to zero, it MUST
3117        //    discard the packet and originate an ICMPv6 Time Exceeded
3118        //    message with Code 0 to the source of the packet.
3119        // Don't send a Time Exceeded Message in cases where the netstack is
3120        // enforcing a higher minimum TTL (e.g. as part of a multicast route).
3121        if ttl > 1 {
3122            packet_meta.acknowledge_drop();
3123            return ForwardingAction::SilentlyDrop;
3124        }
3125
3126        core_ctx.increment_both(inbound_device, |c| &c.ttl_expired);
3127
3128        let marks = packet_meta.marks;
3129        packet_meta.acknowledge_drop();
3130
3131        // Construct and send the appropriate ICMP error for the IP version.
3132        match IcmpErrorSender::new(
3133            core_ctx,
3134            I::IcmpError::ttl_expired(),
3135            &packet,
3136            frame_dst,
3137            inbound_device,
3138            marks,
3139        ) {
3140            Some(icmp_sender) => return ForwardingAction::DropWithIcmpError(icmp_sender),
3141            None => return ForwardingAction::SilentlyDrop,
3142        }
3143    }
3144
3145    trace!("determine_ip_packet_forwarding_action: adequate TTL");
3146
3147    // For IPv6 packets, handle extension headers first.
3148    //
3149    // Any previous handling of extension headers was done under the
3150    // assumption that we are the final destination of the packet. Now that
3151    // we know we're forwarding, we need to re-examine them.
3152    let maybe_ipv6_packet_action = I::map_ip_in(
3153        &packet,
3154        |_packet| None,
3155        |packet| {
3156            Some(ipv6::handle_extension_headers(core_ctx, inbound_device, frame_dst, packet, false))
3157        },
3158    );
3159    match maybe_ipv6_packet_action {
3160        None => {} // NB: Ipv4 case.
3161        Some(Ipv6PacketAction::_Discard) => {
3162            core_ctx.increment_both(inbound_device, |c| {
3163                #[derive(GenericOverIp)]
3164                #[generic_over_ip(I, Ip)]
3165                struct InCounters<'a, I: IpLayerIpExt>(
3166                    &'a <I::RxCounters as CounterCollectionSpec>::CounterCollection<Counter>,
3167                );
3168                I::map_ip_in::<_, _>(
3169                    InCounters(&c.version_rx),
3170                    |_counters| {
3171                        unreachable!(
3172                            "`I` must be `Ipv6` because we're handling IPv6 extension headers"
3173                        )
3174                    },
3175                    |InCounters(counters)| &counters.extension_header_discard,
3176                )
3177            });
3178            trace!(
3179                "determine_ip_packet_forwarding_action: handled IPv6 extension headers: \
3180                discarding packet"
3181            );
3182            packet_meta.acknowledge_drop();
3183            return ForwardingAction::SilentlyDrop;
3184        }
3185        Some(Ipv6PacketAction::Continue) => {
3186            trace!(
3187                "determine_ip_packet_forwarding_action: handled IPv6 extension headers: \
3188                forwarding packet"
3189            );
3190        }
3191        Some(Ipv6PacketAction::ProcessFragment) => {
3192            unreachable!(
3193                "When forwarding packets, we should only ever look at the hop by hop \
3194                    options extension header (if present)"
3195            )
3196        }
3197    };
3198
3199    match core_ctx.filter_handler().forwarding_hook(
3200        I::as_filter_packet(&mut packet),
3201        inbound_device,
3202        outbound_device,
3203        &mut packet_meta,
3204    ) {
3205        filter::Verdict::Stop(filter::DropOrReject::Drop) => {
3206            packet_meta.acknowledge_drop();
3207            trace!("determine_ip_packet_forwarding_action: filter verdict: Drop");
3208            return ForwardingAction::SilentlyDrop;
3209        }
3210        filter::Verdict::Stop(filter::DropOrReject::Reject(reject_type)) => {
3211            // TODO(https://fxbug.dev/466098884): Send reject packet.
3212            packet_meta.acknowledge_drop();
3213            trace!(
3214                "determine_ip_packet_forwarding_action: filter verdict: Reject({:?})",
3215                reject_type
3216            );
3217            return ForwardingAction::SilentlyDrop;
3218        }
3219        filter::Verdict::Proceed(filter::Accept) => {}
3220    }
3221
3222    packet.set_ttl(ttl - 1);
3223    let (_, _, proto, parse_meta): (I::Addr, I::Addr, _, _) = packet.into_metadata();
3224    ForwardingAction::Forward(IpPacketForwarder {
3225        inbound_device,
3226        outbound_device,
3227        packet_meta,
3228        src_ip,
3229        dst_ip,
3230        destination,
3231        proto,
3232        parse_meta,
3233        frame_dst,
3234    })
3235}
3236
3237pub(crate) fn send_ip_frame<I, CC, BC, S>(
3238    core_ctx: &mut CC,
3239    bindings_ctx: &mut BC,
3240    device: &CC::DeviceId,
3241    destination: IpPacketDestination<I, &CC::DeviceId>,
3242    mut body: S,
3243    mut packet_metadata: IpLayerPacketMetadata<I, CC::WeakAddressId, BC>,
3244    limit_mtu: Mtu,
3245) -> Result<(), IpSendFrameError<S>>
3246where
3247    I: IpLayerIpExt,
3248    BC: FilterBindingsContext<CC::DeviceId> + TxMetadataBindingsTypes + MarksBindingsContext,
3249    CC: IpLayerEgressContext<I, BC> + IpDeviceMtuContext<I> + IpDeviceAddressIdContext<I>,
3250    S: FragmentableIpSerializer<I, Buffer: BufferMut> + FilterIpPacket<I>,
3251{
3252    let (verdict, proof) = core_ctx.filter_handler().egress_hook(
3253        bindings_ctx,
3254        &mut body,
3255        device,
3256        &mut packet_metadata,
3257    );
3258    match verdict {
3259        filter::Verdict::Stop(filter::DropPacket) => {
3260            packet_metadata.acknowledge_drop();
3261            return Ok(());
3262        }
3263        filter::Verdict::Proceed(filter::Accept) => {}
3264    }
3265
3266    // If the packet is leaving through the loopback device, attempt to extract a
3267    // weak reference to the packet's conntrack entry to plumb that through the
3268    // device layer so it can be reused on ingress to the IP layer.
3269    // TODO(https://fxbug.dev/452980285): Split a frame carrying GSO metadata
3270    // back into `gso_size` segments here. A coalesced frame is larger than the
3271    // MTU by construction, so until then it is sent (and fragmented) whole.
3272    let (conntrack_connection_and_direction, tx_metadata, marks, _socket_cookie, _gso_info) =
3273        packet_metadata.into_parts();
3274    let conntrack_entry = if device.is_loopback() {
3275        conntrack_connection_and_direction
3276            .and_then(|(conn, dir)| WeakConntrackConnection::new(&conn).map(|conn| (conn, dir)))
3277    } else {
3278        None
3279    };
3280
3281    let mut device_layer_marks = Marks::default();
3282    for mark in BC::marks_to_keep_on_egress() {
3283        *device_layer_marks.get_mut(*mark) = *marks.get(*mark);
3284    }
3285
3286    let device_ip_layer_metadata =
3287        DeviceIpLayerMetadata { conntrack_entry, tx_metadata, marks: device_layer_marks };
3288
3289    // The filtering layer may have changed our address. Perform a last moment
3290    // check to protect against sending loopback addresses on the wire for
3291    // non-loopback devices, which is an RFC violation.
3292    if !device.is_loopback()
3293        && (I::LOOPBACK_SUBNET.contains(&body.src_addr())
3294            || I::LOOPBACK_SUBNET.contains(&body.dst_addr()))
3295    {
3296        core_ctx.increment_both(device, |c| &c.tx_illegal_loopback_address);
3297        return Err(IpSendFrameError {
3298            serializer: body,
3299            error: IpSendFrameErrorReason::IllegalLoopbackAddress,
3300        });
3301    }
3302
3303    // Use the minimum MTU between the target device and the requested mtu.
3304    let mtu = limit_mtu.min(core_ctx.get_mtu(device));
3305
3306    let body = body.with_size_limit(mtu.into());
3307
3308    let fits_mtu = match body.serialize_new_buf(
3309        &mut NetworkSerializationContext::default(),
3310        PacketConstraints::UNCONSTRAINED,
3311        AlwaysFailBufferAlloc,
3312    ) {
3313        // We hit the allocator that refused to allocate new data, which
3314        // means the MTU is respected.
3315        Err(SerializeError::Alloc(())) => true,
3316        // MTU failure, we should try to fragment.
3317        Err(SerializeError::SizeLimitExceeded) => false,
3318    };
3319
3320    if fits_mtu {
3321        return core_ctx
3322            .send_ip_frame(bindings_ctx, device, destination, device_ip_layer_metadata, body, proof)
3323            .map_err(|ErrorAndSerializer { serializer, error }| IpSendFrameError {
3324                serializer: serializer.into_inner(),
3325                error: error.into(),
3326            });
3327    }
3328
3329    // Body doesn't fit MTU, we must fragment this serializer in order to send
3330    // it out.
3331    core_ctx.increment_both(device, |c| &c.fragmentation.fragmentation_required);
3332
3333    // Taken on the last frame.
3334    let mut device_ip_layer_metadata = Some(device_ip_layer_metadata);
3335    let body = body.into_inner();
3336    let result = match IpFragmenter::new(bindings_ctx, &body, mtu) {
3337        Ok(mut fragmenter) => loop {
3338            let (fragment, has_more) = match fragmenter.next() {
3339                None => break Ok(()),
3340                Some(f) => f,
3341            };
3342
3343            // TODO(https://fxbug.dev/391953082): We should penalize sockets
3344            // via the tx metadata when we incur IP fragmentation instead of
3345            // just attaching the ownership to the last fragment. For now, we
3346            // attach the tx metadata to the last frame only.
3347            let device_ip_layer_metadata = if has_more {
3348                // Unwrap here because only the last frame can take it.
3349                let device_ip_layer_metadata = device_ip_layer_metadata.as_ref().unwrap();
3350                DeviceIpLayerMetadata {
3351                    conntrack_entry: device_ip_layer_metadata.conntrack_entry.clone(),
3352                    tx_metadata: Default::default(),
3353                    marks: device_ip_layer_metadata.marks,
3354                }
3355            } else {
3356                // Unwrap here because the last frame can only happen once.
3357                device_ip_layer_metadata.take().unwrap()
3358            };
3359
3360            match core_ctx.send_ip_frame(
3361                bindings_ctx,
3362                device,
3363                destination.clone(),
3364                device_ip_layer_metadata,
3365                fragment,
3366                proof.clone_for_fragmentation(),
3367            ) {
3368                Ok(()) => {
3369                    core_ctx.increment_both(device, |c| &c.fragmentation.fragments);
3370                }
3371                Err(ErrorAndSerializer { serializer: _, error }) => {
3372                    core_ctx
3373                        .increment_both(device, |c| &c.fragmentation.error_fragmented_serializer);
3374                    break Err(error);
3375                }
3376            }
3377        },
3378        Err(e) => {
3379            core_ctx.increment_both(device, |c| &c.fragmentation.error_counter(&e));
3380            Err(SendFrameErrorReason::SizeConstraintsViolation)
3381        }
3382    };
3383    result.map_err(|e| IpSendFrameError { serializer: body, error: e.into() })
3384}
3385
3386/// A buffer allocator that always fails to allocate a new buffer.
3387///
3388/// Can be used to check for packet size constraints in serializer without in
3389/// fact serializing the buffer.
3390struct AlwaysFailBufferAlloc;
3391
3392impl LayoutBufferAlloc<!> for AlwaysFailBufferAlloc {
3393    type Error = ();
3394    fn layout_alloc(self, _prefix: usize, _body: usize, _suffix: usize) -> Result<!, Self::Error> {
3395        Err(())
3396    }
3397}
3398
3399/// Drop a packet and undo the effects of parsing it.
3400///
3401/// `drop_packet_and_undo_parse!` takes a `$packet` and a `$buffer` which the
3402/// packet was parsed from. It saves the results of the `src_ip()`, `dst_ip()`,
3403/// `proto()`, and `parse_metadata()` methods. It drops `$packet` and uses the
3404/// result of `parse_metadata()` to undo the effects of parsing the packet.
3405/// Finally, it returns the source IP, destination IP, protocol, and parse
3406/// metadata.
3407macro_rules! drop_packet_and_undo_parse {
3408    ($packet:expr, $buffer:expr) => {{
3409        let (src_ip, dst_ip, proto, meta) = $packet.into_metadata();
3410        $buffer.undo_parse(meta);
3411        (src_ip, dst_ip, proto, meta)
3412    }};
3413}
3414
3415/// The result of calling [`process_fragment`], depending on what action needs
3416/// to be taken by the caller.
3417enum ProcessFragmentResult<'a, I: IpLayerIpExt> {
3418    /// Processing of the packet is complete and no more action should be
3419    /// taken.
3420    Done,
3421
3422    /// Reassembly is not needed. The returned packet is the same one that was
3423    /// passed in the call to [`process_fragment`].
3424    NotNeeded(I::Packet<&'a mut [u8]>),
3425
3426    /// A packet was successfully reassembled into the provided buffer. If a
3427    /// parsed packet is needed, then the caller must perform that parsing.
3428    Reassembled { buffer: Vec<u8>, max_fragment_len: usize },
3429}
3430
3431/// Process a fragment and reassemble if required.
3432///
3433/// Attempts to process a potential fragment packet and reassemble if we are
3434/// ready to do so. Returns an enum to the caller with the result of processing
3435/// the potential fragment.
3436fn process_fragment<'a, I, CC, BC>(
3437    core_ctx: &mut CC,
3438    bindings_ctx: &mut BC,
3439    device: &CC::DeviceId,
3440    packet: I::Packet<&'a mut [u8]>,
3441) -> ProcessFragmentResult<'a, I>
3442where
3443    I: IpLayerIpExt,
3444    for<'b> I::Packet<&'b mut [u8]>: FragmentablePacket,
3445    CC: IpLayerIngressContext<I, BC>,
3446    BC: IpLayerBindingsContext<I, CC::DeviceId>,
3447{
3448    match FragmentHandler::<I, _>::process_fragment::<&mut [u8]>(core_ctx, bindings_ctx, packet) {
3449        // Handle the packet right away since reassembly is not needed.
3450        FragmentProcessingState::NotNeeded(packet) => {
3451            trace!("receive_ip_packet: not fragmented");
3452            ProcessFragmentResult::NotNeeded(packet)
3453        }
3454        // Ready to reassemble a packet.
3455        FragmentProcessingState::Ready { key, packet_len } => {
3456            trace!("receive_ip_packet: fragmented, ready for reassembly");
3457            // Allocate a buffer of `packet_len` bytes.
3458            let mut buffer = Buf::new(alloc::vec![0; packet_len], ..);
3459
3460            // Attempt to reassemble the packet.
3461            let reassemble_result = match FragmentHandler::<I, _>::reassemble_packet(
3462                core_ctx,
3463                bindings_ctx,
3464                &key,
3465                buffer.buffer_view_mut(),
3466            ) {
3467                // Successfully reassembled the packet, handle it.
3468                Ok(max_fragment_len) => ProcessFragmentResult::Reassembled {
3469                    buffer: buffer.into_inner(),
3470                    max_fragment_len,
3471                },
3472                Err(e) => {
3473                    core_ctx.increment_both(device, |c| &c.fragment_reassembly_error);
3474                    debug!("receive_ip_packet: fragmented, failed to reassemble: {:?}", e);
3475                    ProcessFragmentResult::Done
3476                }
3477            };
3478            reassemble_result
3479        }
3480        // Cannot proceed since we need more fragments before we
3481        // can reassemble a packet.
3482        FragmentProcessingState::NeedMoreFragments => {
3483            core_ctx.increment_both(device, |c| &c.need_more_fragments);
3484            trace!("receive_ip_packet: fragmented, need more before reassembly");
3485            ProcessFragmentResult::Done
3486        }
3487        // TODO(ghanan): Handle invalid fragments.
3488        FragmentProcessingState::InvalidFragment => {
3489            core_ctx.increment_both(device, |c| &c.invalid_fragment);
3490            trace!("receive_ip_packet: fragmented, invalid");
3491            ProcessFragmentResult::Done
3492        }
3493        FragmentProcessingState::OutOfMemory => {
3494            core_ctx.increment_both(device, |c| &c.fragment_cache_full);
3495            trace!("receive_ip_packet: fragmented, dropped because OOM");
3496            ProcessFragmentResult::Done
3497        }
3498    }
3499}
3500
3501// TODO(joshlf): Can we turn `try_parse_ip_packet` into a function? So far, I've
3502// been unable to get the borrow checker to accept it.
3503
3504/// Try to parse an IP packet from a buffer.
3505///
3506/// If parsing fails, return the buffer to its original state so that its
3507/// contents can be used to send an ICMP error message. When invoked, the macro
3508/// expands to an expression whose type is `Result<P, P::Error>`, where `P` is
3509/// the parsed packet type.
3510macro_rules! try_parse_ip_packet {
3511    ($buffer:expr) => {{
3512        let p_len = $buffer.prefix_len();
3513        let s_len = $buffer.suffix_len();
3514
3515        let result = $buffer.parse_mut();
3516
3517        if let Err(err) = result {
3518            // Revert `buffer` to it's original state.
3519            let n_p_len = $buffer.prefix_len();
3520            let n_s_len = $buffer.suffix_len();
3521
3522            if n_p_len > p_len {
3523                $buffer.grow_front(n_p_len - p_len);
3524            }
3525
3526            if n_s_len > s_len {
3527                $buffer.grow_back(n_s_len - s_len);
3528            }
3529
3530            Err(err)
3531        } else {
3532            result
3533        }
3534    }};
3535}
3536
3537/// Clone an IP packet so that it may be delivered to a multicast route target.
3538///
3539/// Note: We must copy the underlying data here, as the filtering
3540/// engine may uniquely modify each instance as part of
3541/// performing forwarding.
3542///
3543/// In the future there are potential optimizations we could
3544/// pursue, including:
3545///   * Copy-on-write semantics for the buffer/packet so that
3546///     copies of the underlying data are done on an as-needed
3547///     basis.
3548///   * Avoid reparsing the IP packet. Because we're parsing an
3549///     exact copy of a known good packet, it would be safe to
3550///     adopt the data as an IP packet without performing any
3551///     validation.
3552// NB: This is a macro, not a function, because Rust's "move" semantics prevent
3553// us from returning both a buffer and a packet referencing that buffer.
3554macro_rules! clone_packet_for_mcast_forwarding {
3555    {let ($new_data:ident, $new_buffer:ident, $new_packet:ident) = $packet:ident} => {
3556        let mut $new_data = $packet.to_vec();
3557        let mut $new_buffer: Buf<&mut [u8]> = Buf::new($new_data.as_mut(), ..);
3558        let $new_packet = try_parse_ip_packet!($new_buffer).unwrap();
3559    };
3560}
3561
3562/// Receive an IPv4 packet from a device.
3563///
3564/// `frame_dst` specifies how this packet was received; see [`FrameDestination`]
3565/// for options.
3566pub fn receive_ipv4_packet<
3567    BC: IpLayerBindingsContext<Ipv4, CC::DeviceId>,
3568    B: BufferMut,
3569    CC: IpLayerIngressContext<Ipv4, BC>,
3570>(
3571    core_ctx: &mut CC,
3572    bindings_ctx: &mut BC,
3573    device: &CC::DeviceId,
3574    frame_dst: Option<LocalFrameDestination>,
3575    device_ip_layer_metadata: DeviceIpLayerMetadata<BC>,
3576    parsing_context: NetworkParsingContext,
3577    gso_info: Option<GsoInfo>,
3578    buffer: B,
3579) {
3580    if !core_ctx.is_ip_device_enabled(&device) {
3581        return;
3582    }
3583
3584    // This is required because we may need to process the buffer that was
3585    // passed in or a reassembled one, which have different types.
3586    let mut buffer: packet::Either<B, Buf<Vec<u8>>> = packet::Either::A(buffer);
3587
3588    core_ctx.increment_both(device, |c| &c.receive_ip_packet);
3589    trace!("receive_ip_packet({device:?})");
3590
3591    let packet: Ipv4Packet<_> = match try_parse_ip_packet!(buffer) {
3592        Ok(packet) => packet,
3593        Err(ParseError::Format)
3594        | Err(ParseError::Checksum)
3595        | Err(ParseError::NotSupported)
3596        | Err(ParseError::NotExpected) => {
3597            core_ctx.increment_both(device, |c| &c.unparsable_packet);
3598            return;
3599        }
3600    };
3601
3602    // We verify these properties later by actually creating the corresponding
3603    // witness types after the INGRESS filtering hook, but we keep these checks
3604    // here as an optimization to return early and save some work.
3605    if packet.src_ipv4().is_none() {
3606        debug!(
3607            "receive_ipv4_packet: received packet from invalid source {}; dropping",
3608            packet.src_ip()
3609        );
3610        core_ctx.increment_both(device, |c| &c.invalid_source);
3611        return;
3612    };
3613    if !packet.dst_ip().is_specified() {
3614        core_ctx.increment_both(device, |c| &c.unspecified_destination);
3615        debug!("receive_ipv4_packet: Received packet with unspecified destination IP; dropping");
3616        return;
3617    };
3618
3619    // Per RFC 1122, Section 3.2.1.3:
3620    //   Internal host loopback address.  Addresses of this form
3621    //   MUST NOT appear outside a host.
3622    if !device.is_loopback()
3623        && (Ipv4::LOOPBACK_SUBNET.contains(&packet.src_ip())
3624            || Ipv4::LOOPBACK_SUBNET.contains(&packet.dst_ip()))
3625    {
3626        debug!(
3627            "receive_ipv4_packet: received loopback packet (src={}, dst={}) \
3628            on non-loopback interface; dropping",
3629            packet.src_ip(),
3630            packet.dst_ip(),
3631        );
3632        return;
3633    }
3634
3635    // Per RFC 1122 Section 3.2.1.3, broadcast addresses (including limited and
3636    // subnet directed broadcasts) must not be used as source addresses.
3637    if let Some(src_ip) = SpecifiedAddr::new(packet.src_ip()) {
3638        // NOTE: In case the device doesn't have an address, this can only
3639        // detect limited broadcast (255.255.255.255).
3640        let is_broadcast = core_ctx
3641            .address_status_for_device(src_ip, device)
3642            .into_present()
3643            .and_then(|status| status.to_broadcast_marker())
3644            .is_some();
3645
3646        if is_broadcast {
3647            debug!(
3648                "receive_ipv4_packet: received packet from broadcast source {}; dropping",
3649                packet.src_ip()
3650            );
3651            core_ctx.increment_both(device, |c| &c.invalid_source);
3652            return;
3653        }
3654    }
3655
3656    // Reassemble all packets before local delivery or forwarding. Reassembly
3657    // before forwarding is not RFC-compliant, but it's the easiest way to
3658    // ensure that fragments are filtered properly. Linux does this and it
3659    // doesn't seem to create major problems.
3660    //
3661    // TODO(https://fxbug.dev/345814518): Forward fragments without reassembly.
3662    //
3663    // Note, the `process_fragment` function could panic if the packet does not
3664    // have fragment data. However, we are guaranteed that it will not panic
3665    // because the fragment data is in the fixed header so it is always present
3666    // (even if the fragment data has values that implies that the packet is not
3667    // fragmented).
3668    let (mut packet, max_fragment_len) =
3669        match process_fragment(core_ctx, bindings_ctx, device, packet) {
3670            ProcessFragmentResult::Done => return,
3671            ProcessFragmentResult::NotNeeded(packet) => (packet, None),
3672            ProcessFragmentResult::Reassembled { buffer: buf, max_fragment_len } => {
3673                let buf = Buf::new(buf, ..);
3674                buffer = packet::Either::B(buf);
3675
3676                match buffer.parse_mut() {
3677                    Ok(packet) => (packet, Some(max_fragment_len)),
3678                    Err(err) => {
3679                        core_ctx.increment_both(device, |c| &c.fragment_reassembly_error);
3680                        debug!("receive_ip_packet: fragmented, failed to reassemble: {:?}", err);
3681                        return;
3682                    }
3683                }
3684            }
3685        };
3686
3687    // TODO(ghanan): Act upon options.
3688
3689    let mut packet_metadata = IpLayerPacketMetadata::from_device_ip_layer_metadata(
3690        core_ctx,
3691        device,
3692        device_ip_layer_metadata,
3693        gso_info,
3694    );
3695    let mut filter = core_ctx.filter_handler();
3696    match filter.ingress_hook(bindings_ctx, &mut packet, device, &mut packet_metadata) {
3697        filter::Verdict::Proceed(filter::Accept) => {}
3698        filter::Verdict::Stop(filter::IngressStopReason::Drop) => {
3699            packet_metadata.acknowledge_drop();
3700            return;
3701        }
3702        filter::Verdict::Stop(filter::IngressStopReason::TransparentLocalDelivery {
3703            addr,
3704            port,
3705        }) => {
3706            // Drop the filter handler since it holds a mutable borrow of `core_ctx`, which
3707            // we need to provide to the packet dispatch function.
3708            drop(filter);
3709
3710            let Some(addr) = SpecifiedAddr::new(addr) else {
3711                core_ctx.increment_both(device, |c| &c.unspecified_destination);
3712                debug!("cannot perform transparent delivery to unspecified destination; dropping");
3713                packet_metadata.acknowledge_drop();
3714                return;
3715            };
3716
3717            let receive_meta = ReceiveIpPacketMeta {
3718                // It's possible that the packet was actually sent to a
3719                // broadcast address, but it doesn't matter here since it's
3720                // being delivered to a transparent proxy.
3721                broadcast: None,
3722                transparent_override: Some(TransparentLocalDelivery { addr, port }),
3723                parsing_context,
3724            };
3725
3726            // Short-circuit the routing process and override local demux, providing a local
3727            // address and port to which the packet should be transparently delivered at the
3728            // transport layer.
3729            dispatch_receive_ipv4_packet(
3730                core_ctx,
3731                bindings_ctx,
3732                device,
3733                frame_dst,
3734                packet,
3735                packet_metadata,
3736                receive_meta,
3737            )
3738            .unwrap_or_else(|icmp_sender| icmp_sender.send(core_ctx, bindings_ctx, buffer));
3739            return;
3740        }
3741    }
3742    // Drop the filter handler since it holds a mutable borrow of `core_ctx`, which
3743    // we need below.
3744    drop(filter);
3745
3746    let Some(src_ip) = packet.src_ipv4() else {
3747        core_ctx.increment_both(device, |c| &c.invalid_source);
3748        debug!(
3749            "receive_ipv4_packet: received packet from invalid source {}; dropping",
3750            packet.src_ip()
3751        );
3752        packet_metadata.acknowledge_drop();
3753        return;
3754    };
3755
3756    let action = receive_ipv4_packet_action(
3757        core_ctx,
3758        bindings_ctx,
3759        device,
3760        &packet,
3761        frame_dst,
3762        &packet_metadata.marks,
3763        max_fragment_len,
3764    );
3765    match action {
3766        ReceivePacketAction::MulticastForward { targets, address_status, dst_ip } => {
3767            // TOOD(https://fxbug.dev/364242513): Support connection tracking of
3768            // the multiplexed flows created by multicast forwarding. Here, we
3769            // use the existing metadata for the first action taken, and then
3770            // a default instance for each subsequent action. The first action
3771            // will populate the conntrack table with an entry, which will then
3772            // be used by all subsequent forwards.
3773            let mut packet_metadata = Some(packet_metadata);
3774            for MulticastRouteTarget { output_interface, min_ttl } in targets.as_ref() {
3775                clone_packet_for_mcast_forwarding! {
3776                    let (copy_of_data, copy_of_buffer, copy_of_packet) = packet
3777                };
3778                determine_ip_packet_forwarding_action::<Ipv4, _, _>(
3779                    core_ctx,
3780                    copy_of_packet,
3781                    packet_metadata.take().unwrap_or_default(),
3782                    Some(*min_ttl),
3783                    device,
3784                    &output_interface,
3785                    IpPacketDestination::from_addr(dst_ip),
3786                    frame_dst,
3787                    src_ip,
3788                    dst_ip,
3789                )
3790                .perform_action_with_buffer(
3791                    core_ctx,
3792                    bindings_ctx,
3793                    copy_of_buffer,
3794                    max_fragment_len,
3795                );
3796            }
3797
3798            // If we also have an interest in the packet, deliver it locally.
3799            if let Some(address_status) = address_status {
3800                let receive_meta = ReceiveIpPacketMeta {
3801                    broadcast: address_status.to_broadcast_marker(),
3802                    transparent_override: None,
3803                    parsing_context,
3804                };
3805                dispatch_receive_ipv4_packet(
3806                    core_ctx,
3807                    bindings_ctx,
3808                    device,
3809                    frame_dst,
3810                    packet,
3811                    packet_metadata.take().unwrap_or_default(),
3812                    receive_meta,
3813                )
3814                .unwrap_or_else(|icmp_sender| icmp_sender.send(core_ctx, bindings_ctx, buffer));
3815            }
3816        }
3817        ReceivePacketAction::Deliver { address_status, internal_forwarding } => {
3818            // NB: when performing internal forwarding, hit the
3819            // forwarding hook.
3820            match internal_forwarding {
3821                InternalForwarding::Used(outbound_device) => {
3822                    core_ctx.increment_both(device, |c| &c.forward);
3823                    match core_ctx.filter_handler().forwarding_hook(
3824                        &mut packet,
3825                        device,
3826                        &outbound_device,
3827                        &mut packet_metadata,
3828                    ) {
3829                        filter::Verdict::Stop(filter::DropOrReject::Drop) => {
3830                            packet_metadata.acknowledge_drop();
3831                            return;
3832                        }
3833                        filter::Verdict::Stop(filter::DropOrReject::Reject(_reject_type)) => {
3834                            // TODO(https://fxbug.dev/466098884): Send reject packet.
3835                            packet_metadata.acknowledge_drop();
3836                            return;
3837                        }
3838                        filter::Verdict::Proceed(filter::Accept) => {}
3839                    }
3840                }
3841                InternalForwarding::NotUsed => {}
3842            }
3843
3844            let receive_meta = ReceiveIpPacketMeta {
3845                broadcast: address_status.to_broadcast_marker(),
3846                transparent_override: None,
3847                parsing_context,
3848            };
3849            dispatch_receive_ipv4_packet(
3850                core_ctx,
3851                bindings_ctx,
3852                device,
3853                frame_dst,
3854                packet,
3855                packet_metadata,
3856                receive_meta,
3857            )
3858            .unwrap_or_else(|icmp_sender| icmp_sender.send(core_ctx, bindings_ctx, buffer));
3859        }
3860        ReceivePacketAction::Forward {
3861            original_dst,
3862            dst: Destination { device: dst_device, next_hop },
3863        } => {
3864            determine_ip_packet_forwarding_action::<Ipv4, _, _>(
3865                core_ctx,
3866                packet,
3867                packet_metadata,
3868                None,
3869                device,
3870                &dst_device,
3871                IpPacketDestination::from_next_hop(next_hop, original_dst),
3872                frame_dst,
3873                src_ip,
3874                original_dst,
3875            )
3876            .perform_action_with_buffer(
3877                core_ctx,
3878                bindings_ctx,
3879                buffer,
3880                max_fragment_len,
3881            );
3882        }
3883        ReceivePacketAction::SendNoRouteToDest { dst: dst_ip } => {
3884            debug!("received IPv4 packet with no known route to destination {}", dst_ip);
3885
3886            let marks = packet_metadata.marks;
3887            packet_metadata.acknowledge_drop();
3888
3889            if let Some(sender) = IcmpErrorSender::new(
3890                core_ctx,
3891                Icmpv4Error::NetUnreachable,
3892                &packet,
3893                frame_dst,
3894                device,
3895                marks,
3896            ) {
3897                sender.send(core_ctx, bindings_ctx, buffer);
3898            }
3899        }
3900        ReceivePacketAction::Drop { reason } => {
3901            let src_ip = packet.src_ip();
3902            let dst_ip = packet.dst_ip();
3903            packet_metadata.acknowledge_drop();
3904            core_ctx.increment_both(device, |c| &c.dropped);
3905            debug!(
3906                "receive_ipv4_packet: dropping packet from {src_ip} to {dst_ip} received on \
3907                {device:?}: {reason:?}",
3908            );
3909        }
3910    }
3911}
3912
3913fn handle_ipv6_parse_error<BC, B, CC>(
3914    core_ctx: &mut CC,
3915    bindings_ctx: &mut BC,
3916    device: &CC::DeviceId,
3917    frame_dst: Option<LocalFrameDestination>,
3918    device_ip_layer_metadata: DeviceIpLayerMetadata<BC>,
3919    mut buffer: B,
3920    error: Ipv6ParseError,
3921) where
3922    BC: IpLayerBindingsContext<Ipv6, CC::DeviceId>,
3923    B: BufferMut,
3924    CC: IpLayerIngressContext<Ipv6, BC>,
3925{
3926    // Conditionally send an ICMP response if we encountered a parameter
3927    // problem error when parsing an IPv6 packet. Note, we do not always
3928    // send back an ICMP response as it can be used as an attack vector for
3929    // DDoS attacks. We only send back an ICMP response if the RFC requires
3930    // that we MUST send one, as noted by `must_send_icmp` and `action`.
3931    let Ipv6ParseError::ParameterProblem { src_ip, dst_ip, code, pointer, must_send_icmp, action } =
3932        error
3933    else {
3934        core_ctx.increment_both(device, |c| &c.unparsable_packet);
3935        debug!("receive_ipv6_packet: Failed to parse IPv6 packet: {:?}", error);
3936        return;
3937    };
3938    if !must_send_icmp || !action.should_send_icmp(&dst_ip) {
3939        return;
3940    }
3941    core_ctx.increment_both(device, |c| &c.parameter_problem);
3942    let dst_ip = match SocketIpAddr::new(dst_ip) {
3943        Some(ip) => ip,
3944        None => {
3945            core_ctx.increment_both(device, |c| &c.unspecified_destination);
3946            debug!("receive_ipv6_packet: Dropping packet with unspecified destination IP");
3947            return;
3948        }
3949    };
3950
3951    let src_ip = match Ipv6SourceAddr::new(src_ip) {
3952        None => {
3953            core_ctx.increment_both(device, |c| &c.invalid_source);
3954            return;
3955        }
3956        Some(Ipv6SourceAddr::Unspecified) => {
3957            core_ctx.increment_both(device, |c| &c.unspecified_source);
3958            return;
3959        }
3960        Some(Ipv6SourceAddr::Unicast(src_ip)) => {
3961            SocketIpAddr::new_from_ipv6_non_mapped_unicast(src_ip)
3962        }
3963    };
3964
3965    // Try raw parser to find main packet protocol and body offset. If this
3966    // fails as well then we can't send an ICMP error message.
3967    let raw_packet: Ipv6PacketRaw<_> = match try_parse_ip_packet!(buffer) {
3968        Ok(packet) => packet,
3969        Err(error) => {
3970            core_ctx.increment_both(device, |c| &c.unparsable_packet);
3971            debug!("receive_ipv6_packet: Failed to parse IPv6 packet: {:?}", error);
3972            return;
3973        }
3974    };
3975    let proto = match raw_packet.proto() {
3976        Ok(proto) => proto,
3977        Err(error) => {
3978            core_ctx.increment_both(device, |c| &c.unparsable_packet);
3979            debug!("receive_ipv6_packet: Failed to get protocol from IPv6 packet: {:?}", error);
3980            return;
3981        }
3982    };
3983    let parse_metadata = raw_packet.parse_metadata();
3984    let header_len = parse_metadata.header_len();
3985    buffer.undo_parse(parse_metadata);
3986
3987    let err = Icmpv6Error::ParameterProblem {
3988        code,
3989        pointer,
3990        allow_dst_multicast: action.should_send_icmp_to_multicast(),
3991    };
3992
3993    IcmpErrorHandler::<Ipv6, _>::send_icmp_error_message(
3994        core_ctx,
3995        bindings_ctx,
3996        Some(device),
3997        frame_dst,
3998        src_ip,
3999        dst_ip,
4000        buffer,
4001        err,
4002        header_len,
4003        proto,
4004        &device_ip_layer_metadata.marks,
4005    );
4006}
4007
4008/// Receive an IPv6 packet from a device.
4009///
4010/// `frame_dst` specifies how this packet was received; see [`FrameDestination`]
4011/// for options.
4012pub fn receive_ipv6_packet<
4013    BC: IpLayerBindingsContext<Ipv6, CC::DeviceId>,
4014    B: BufferMut,
4015    CC: IpLayerIngressContext<Ipv6, BC>,
4016>(
4017    core_ctx: &mut CC,
4018    bindings_ctx: &mut BC,
4019    device: &CC::DeviceId,
4020    frame_dst: Option<LocalFrameDestination>,
4021    device_ip_layer_metadata: DeviceIpLayerMetadata<BC>,
4022    parsing_context: NetworkParsingContext,
4023    gso_info: Option<GsoInfo>,
4024    buffer: B,
4025) {
4026    if !core_ctx.is_ip_device_enabled(&device) {
4027        return;
4028    }
4029
4030    // This is required because we may need to process the buffer that was
4031    // passed in or a reassembled one, which have different types.
4032    let mut buffer: packet::Either<B, Buf<Vec<u8>>> = packet::Either::A(buffer);
4033
4034    core_ctx.increment_both(device, |c| &c.receive_ip_packet);
4035    trace!("receive_ipv6_packet({:?})", device);
4036
4037    let packet: Ipv6Packet<_> = match try_parse_ip_packet!(buffer) {
4038        Ok(packet) => packet,
4039        Err(error) => {
4040            handle_ipv6_parse_error(
4041                core_ctx,
4042                bindings_ctx,
4043                device,
4044                frame_dst,
4045                device_ip_layer_metadata,
4046                buffer,
4047                error,
4048            );
4049            return;
4050        }
4051    };
4052
4053    trace!("receive_ipv6_packet: parsed packet: {:?}", packet);
4054
4055    // TODO(ghanan): Act upon extension headers.
4056
4057    // We verify these properties later by actually creating the corresponding
4058    // witness types after the INGRESS filtering hook, but we keep these checks
4059    // here as an optimization to return early and save some work.
4060    if packet.src_ipv6().is_none() {
4061        debug!(
4062            "receive_ipv6_packet: received packet from invalid source {}; dropping",
4063            packet.src_ip()
4064        );
4065        core_ctx.increment_both(device, |c| &c.invalid_source);
4066        return;
4067    };
4068    if !packet.dst_ip().is_specified() {
4069        core_ctx.increment_both(device, |c| &c.unspecified_destination);
4070        debug!("receive_ipv6_packet: Received packet with unspecified destination IP; dropping");
4071        return;
4072    };
4073
4074    // Per RFC 4291, Section 2.5.3:
4075    //   The loopback address must not be used as the source address in IPv6
4076    //   packets that are sent outside of a single node.  An IPv6 packet with
4077    //   a destination address of loopback must never be sent outside of a
4078    //   single node and must never be forwarded by an IPv6 router.  A packet
4079    //   received on an interface with a destination address of loopback must
4080    //   be dropped.
4081    if !device.is_loopback()
4082        && (Ipv6::LOOPBACK_SUBNET.contains(&packet.src_ip())
4083            || Ipv6::LOOPBACK_SUBNET.contains(&packet.dst_ip()))
4084    {
4085        debug!(
4086            "receive_ipv6_packet: received loopback packet (src={}, dst={}) \
4087            on non-loopback interface; dropping",
4088            packet.src_ip(),
4089            packet.dst_ip(),
4090        );
4091        return;
4092    }
4093
4094    // Reassemble all packets before local delivery or forwarding. Reassembly
4095    // before forwarding is not RFC-compliant, but it's the easiest way to
4096    // ensure that fragments are filtered properly. Linux does this and it
4097    // doesn't seem to create major problems.
4098    //
4099    // TODO(https://fxbug.dev/345814518): Forward fragments without reassembly.
4100    //
4101    // delivery_extension_header_action is used to prevent looking at the
4102    // extension headers twice when a non-fragmented packet is delivered
4103    // locally.
4104    let (mut packet, delivery_extension_header_action, max_fragment_len) =
4105        match ipv6::handle_extension_headers(core_ctx, device, frame_dst, &packet, true) {
4106            Ipv6PacketAction::_Discard => {
4107                core_ctx.increment_both(device, |c| &c.version_rx.extension_header_discard);
4108                trace!("receive_ipv6_packet: handled IPv6 extension headers: discarding packet");
4109                return;
4110            }
4111            Ipv6PacketAction::Continue => {
4112                trace!("receive_ipv6_packet: handled IPv6 extension headers: dispatching packet");
4113                (packet, Some(Ipv6PacketAction::Continue), None)
4114            }
4115            Ipv6PacketAction::ProcessFragment => {
4116                trace!(
4117                    "receive_ipv6_packet: handled IPv6 extension headers: handling \
4118                    fragmented packet"
4119                );
4120
4121                // Note, `IpPacketFragmentCache::process_fragment`
4122                // could panic if the packet does not have fragment data.
4123                // However, we are guaranteed that it will not panic for an
4124                // IPv6 packet because the fragment data is in an (optional)
4125                // fragment extension header which we attempt to handle by
4126                // calling `ipv6::handle_extension_headers`. We will only
4127                // end up here if its return value is
4128                // `Ipv6PacketAction::ProcessFragment` which is only
4129                // possible when the packet has the fragment extension
4130                // header (even if the fragment data has values that implies
4131                // that the packet is not fragmented).
4132                match process_fragment(core_ctx, bindings_ctx, device, packet) {
4133                    ProcessFragmentResult::Done => return,
4134                    ProcessFragmentResult::NotNeeded(packet) => {
4135                        // While strange, it's possible for there to be a Fragment
4136                        // header that says the packet doesn't need defragmentation.
4137                        // As per RFC 8200 4.5:
4138                        //
4139                        //   If the fragment is a whole datagram (that is, both the
4140                        //   Fragment Offset field and the M flag are zero), then it
4141                        //   does not need any further reassembly and should be
4142                        //   processed as a fully reassembled packet (i.e., updating
4143                        //   Next Header, adjust Payload Length, removing the
4144                        //   Fragment header, etc.).
4145                        //
4146                        // In this case, we're not technically reassembling the
4147                        // packet, since, per the RFC, that would mean removing the
4148                        // Fragment header.
4149                        (packet, Some(Ipv6PacketAction::Continue), None)
4150                    }
4151                    ProcessFragmentResult::Reassembled { buffer: buf, max_fragment_len } => {
4152                        let buf = Buf::new(buf, ..);
4153                        buffer = packet::Either::B(buf);
4154
4155                        match buffer.parse_mut() {
4156                            Ok(packet) => (packet, None, Some(max_fragment_len)),
4157                            Err(err) => {
4158                                core_ctx.increment_both(device, |c| &c.fragment_reassembly_error);
4159                                debug!(
4160                                    "receive_ip_packet: fragmented, failed to reassemble: {:?}",
4161                                    err
4162                                );
4163                                return;
4164                            }
4165                        }
4166                    }
4167                }
4168            }
4169        };
4170
4171    let mut packet_metadata = IpLayerPacketMetadata::from_device_ip_layer_metadata(
4172        core_ctx,
4173        device,
4174        device_ip_layer_metadata,
4175        gso_info,
4176    );
4177    let mut filter = core_ctx.filter_handler();
4178
4179    match filter.ingress_hook(bindings_ctx, &mut packet, device, &mut packet_metadata) {
4180        filter::Verdict::Proceed(filter::Accept) => {}
4181        filter::Verdict::Stop(filter::IngressStopReason::Drop) => {
4182            packet_metadata.acknowledge_drop();
4183            return;
4184        }
4185        filter::Verdict::Stop(filter::IngressStopReason::TransparentLocalDelivery {
4186            addr,
4187            port,
4188        }) => {
4189            // Drop the filter handler since it holds a mutable borrow of `core_ctx`, which
4190            // we need to provide to the packet dispatch function.
4191            drop(filter);
4192
4193            let Some(addr) = SpecifiedAddr::new(addr) else {
4194                core_ctx.increment_both(device, |c| &c.unspecified_destination);
4195                debug!("cannot perform transparent delivery to unspecified destination; dropping");
4196                packet_metadata.acknowledge_drop();
4197                return;
4198            };
4199
4200            let receive_meta = ReceiveIpPacketMeta {
4201                broadcast: None,
4202                transparent_override: Some(TransparentLocalDelivery { addr, port }),
4203                parsing_context,
4204            };
4205
4206            // Short-circuit the routing process and override local demux, providing a local
4207            // address and port to which the packet should be transparently delivered at the
4208            // transport layer.
4209            dispatch_receive_ipv6_packet(
4210                core_ctx,
4211                bindings_ctx,
4212                device,
4213                frame_dst,
4214                packet,
4215                packet_metadata,
4216                receive_meta,
4217            )
4218            .unwrap_or_else(|icmp_sender| icmp_sender.send(core_ctx, bindings_ctx, buffer));
4219            return;
4220        }
4221    }
4222    // Drop the filter handler since it holds a mutable borrow of `core_ctx`, which
4223    // we need below.
4224    drop(filter);
4225
4226    let Some(src_ip) = packet.src_ipv6() else {
4227        debug!(
4228            "receive_ipv6_packet: received packet from invalid source {}; dropping",
4229            packet.src_ip()
4230        );
4231        core_ctx.increment_both(device, |c| &c.invalid_source);
4232        packet_metadata.acknowledge_drop();
4233        return;
4234    };
4235
4236    match receive_ipv6_packet_action(
4237        core_ctx,
4238        bindings_ctx,
4239        device,
4240        &packet,
4241        frame_dst,
4242        &packet_metadata.marks,
4243        max_fragment_len,
4244    ) {
4245        ReceivePacketAction::MulticastForward { targets, address_status, dst_ip } => {
4246            // TOOD(https://fxbug.dev/364242513): Support connection tracking of
4247            // the multiplexed flows created by multicast forwarding. Here, we
4248            // use the existing metadata for the first action taken, and then
4249            // a default instance for each subsequent action. The first action
4250            // will populate the conntrack table with an entry, which will then
4251            // be used by all subsequent forwards.
4252            let mut packet_metadata = Some(packet_metadata);
4253            for MulticastRouteTarget { output_interface, min_ttl } in targets.as_ref() {
4254                clone_packet_for_mcast_forwarding! {
4255                    let (copy_of_data, copy_of_buffer, copy_of_packet) = packet
4256                };
4257                determine_ip_packet_forwarding_action::<Ipv6, _, _>(
4258                    core_ctx,
4259                    copy_of_packet,
4260                    packet_metadata.take().unwrap_or_default(),
4261                    Some(*min_ttl),
4262                    device,
4263                    &output_interface,
4264                    IpPacketDestination::from_addr(dst_ip),
4265                    frame_dst,
4266                    src_ip,
4267                    dst_ip,
4268                )
4269                .perform_action_with_buffer(
4270                    core_ctx,
4271                    bindings_ctx,
4272                    copy_of_buffer,
4273                    max_fragment_len,
4274                );
4275            }
4276
4277            // If we also have an interest in the packet, deliver it locally.
4278            if let Some(_) = address_status {
4279                let receive_meta = ReceiveIpPacketMeta {
4280                    broadcast: None,
4281                    transparent_override: None,
4282                    parsing_context,
4283                };
4284
4285                dispatch_receive_ipv6_packet(
4286                    core_ctx,
4287                    bindings_ctx,
4288                    device,
4289                    frame_dst,
4290                    packet,
4291                    packet_metadata.take().unwrap_or_default(),
4292                    receive_meta,
4293                )
4294                .unwrap_or_else(|icmp_sender| icmp_sender.send(core_ctx, bindings_ctx, buffer));
4295            }
4296        }
4297        ReceivePacketAction::Deliver { address_status: _, internal_forwarding } => {
4298            trace!("receive_ipv6_packet: delivering locally");
4299
4300            let action = if let Some(action) = delivery_extension_header_action {
4301                action
4302            } else {
4303                ipv6::handle_extension_headers(core_ctx, device, frame_dst, &packet, true)
4304            };
4305            match action {
4306                Ipv6PacketAction::_Discard => {
4307                    core_ctx.increment_both(device, |c| &c.version_rx.extension_header_discard);
4308                    trace!(
4309                        "receive_ipv6_packet: handled IPv6 extension headers: discarding packet"
4310                    );
4311                    packet_metadata.acknowledge_drop();
4312                }
4313                Ipv6PacketAction::Continue => {
4314                    trace!(
4315                        "receive_ipv6_packet: handled IPv6 extension headers: dispatching packet"
4316                    );
4317
4318                    // NB: when performing internal forwarding, hit the
4319                    // forwarding hook.
4320                    match internal_forwarding {
4321                        InternalForwarding::Used(outbound_device) => {
4322                            core_ctx.increment_both(device, |c| &c.forward);
4323                            match core_ctx.filter_handler().forwarding_hook(
4324                                &mut packet,
4325                                device,
4326                                &outbound_device,
4327                                &mut packet_metadata,
4328                            ) {
4329                                filter::Verdict::Stop(filter::DropOrReject::Drop) => {
4330                                    packet_metadata.acknowledge_drop();
4331                                    return;
4332                                }
4333                                filter::Verdict::Stop(filter::DropOrReject::Reject(
4334                                    _reject_type,
4335                                )) => {
4336                                    // TODO(https://fxbug.dev/466098884): Send reject packet.
4337                                    packet_metadata.acknowledge_drop();
4338                                    return;
4339                                }
4340                                filter::Verdict::Proceed(filter::Accept) => {}
4341                            }
4342                        }
4343                        InternalForwarding::NotUsed => {}
4344                    }
4345
4346                    let meta = ReceiveIpPacketMeta {
4347                        broadcast: None,
4348                        transparent_override: None,
4349                        parsing_context,
4350                    };
4351                    dispatch_receive_ipv6_packet(
4352                        core_ctx,
4353                        bindings_ctx,
4354                        device,
4355                        frame_dst,
4356                        packet,
4357                        packet_metadata,
4358                        meta,
4359                    )
4360                    .unwrap_or_else(|icmp_sender| icmp_sender.send(core_ctx, bindings_ctx, buffer));
4361                }
4362                Ipv6PacketAction::ProcessFragment => {
4363                    debug!("receive_ipv6_packet: found fragment header after reassembly; dropping");
4364                    packet_metadata.acknowledge_drop();
4365                }
4366            }
4367        }
4368        ReceivePacketAction::Forward {
4369            original_dst,
4370            dst: Destination { device: dst_device, next_hop },
4371        } => {
4372            determine_ip_packet_forwarding_action::<Ipv6, _, _>(
4373                core_ctx,
4374                packet,
4375                packet_metadata,
4376                None,
4377                device,
4378                &dst_device,
4379                IpPacketDestination::from_next_hop(next_hop, original_dst),
4380                frame_dst,
4381                src_ip,
4382                original_dst,
4383            )
4384            .perform_action_with_buffer(
4385                core_ctx,
4386                bindings_ctx,
4387                buffer,
4388                max_fragment_len,
4389            );
4390        }
4391        ReceivePacketAction::SendNoRouteToDest { dst: dst_ip } => {
4392            let (_, _, proto, meta): (Ipv6Addr, Ipv6Addr, _, _) =
4393                drop_packet_and_undo_parse!(packet, buffer);
4394            debug!("received IPv6 packet with no known route to destination {}", dst_ip);
4395            let marks = packet_metadata.marks;
4396            packet_metadata.acknowledge_drop();
4397
4398            let src_ip = match src_ip {
4399                Ipv6SourceAddr::Unspecified => {
4400                    core_ctx.increment_both(device, |c| &c.unspecified_source);
4401                    return;
4402                }
4403                Ipv6SourceAddr::Unicast(src_ip) => {
4404                    SocketIpAddr::new_from_ipv6_non_mapped_unicast(src_ip)
4405                }
4406            };
4407
4408            IcmpErrorHandler::<Ipv6, _>::send_icmp_error_message(
4409                core_ctx,
4410                bindings_ctx,
4411                Some(device),
4412                frame_dst,
4413                src_ip,
4414                SocketIpAddr::new_from_witness(dst_ip),
4415                buffer,
4416                Icmpv6Error::NetUnreachable,
4417                meta.header_len(),
4418                proto,
4419                &marks,
4420            );
4421        }
4422        ReceivePacketAction::Drop { reason } => {
4423            core_ctx.increment_both(device, |c| &c.dropped);
4424            let src_ip = packet.src_ip();
4425            let dst_ip = packet.dst_ip();
4426            packet_metadata.acknowledge_drop();
4427            debug!(
4428                "receive_ipv6_packet: dropping packet from {src_ip} to {dst_ip} received on \
4429                {device:?}: {reason:?}",
4430            );
4431        }
4432    }
4433}
4434
4435/// The action to take in order to process a received IP packet.
4436#[derive(Debug, PartialEq)]
4437pub enum ReceivePacketAction<I: BroadcastIpExt + IpLayerIpExt, DeviceId: StrongDeviceIdentifier> {
4438    /// Deliver the packet locally.
4439    Deliver {
4440        /// Status of the receiving IP address.
4441        address_status: I::AddressStatus,
4442        /// `InternalForwarding::Used(d)` if we're delivering the packet as a
4443        /// Weak Host performing internal forwarding via output device `d`.
4444        internal_forwarding: InternalForwarding<DeviceId>,
4445    },
4446
4447    /// Forward the packet to the given destination.
4448    Forward {
4449        /// The original destination IP address of the packet.
4450        original_dst: SpecifiedAddr<I::Addr>,
4451        /// The destination that the packet should be forwarded to.
4452        dst: Destination<I::Addr, DeviceId>,
4453    },
4454
4455    /// A multicast packet that should be forwarded (& optional local delivery).
4456    ///
4457    /// The packet should be forwarded to each of the given targets. This case
4458    /// is only returned when the packet is eligible for multicast forwarding;
4459    /// `Self::Deliver` is used for packets that are ineligible (either because
4460    /// multicast forwarding is disabled, or because there are no applicable
4461    /// multicast routes with which to forward the packet).
4462    MulticastForward {
4463        /// The multicast targets to forward the packet via.
4464        targets: MulticastRouteTargets<DeviceId>,
4465        /// Some if the host is a member of the multicast group and the packet
4466        /// should be delivered locally (in addition to forwarding).
4467        address_status: Option<I::AddressStatus>,
4468        /// The multicast address the packet should be forwarded to.
4469        dst_ip: SpecifiedAddr<I::Addr>,
4470    },
4471
4472    /// Send a Destination Unreachable ICMP error message to the packet's sender
4473    /// and drop the packet.
4474    ///
4475    /// For ICMPv4, use the code "net unreachable". For ICMPv6, use the code "no
4476    /// route to destination".
4477    SendNoRouteToDest {
4478        /// The destination IP Address to which there was no route.
4479        dst: NonMappedAddr<SpecifiedAddr<I::Addr>>,
4480    },
4481
4482    /// Silently drop the packet.
4483    ///
4484    /// `reason` describes why the packet was dropped.
4485    #[allow(missing_docs)]
4486    Drop { reason: DropReason },
4487}
4488
4489// It's possible that there is more than one device with the address
4490// present. Prefer any address status over `UnicastTentative`.
4491fn choose_highest_priority_address_status<I: IpLayerIpExt>(
4492    address_statuses: impl Iterator<Item = I::AddressStatus>,
4493) -> Option<I::AddressStatus> {
4494    address_statuses.max_by_key(|status| {
4495        #[derive(GenericOverIp)]
4496        #[generic_over_ip(I, Ip)]
4497        struct Wrap<'a, I: IpLayerIpExt>(&'a I::AddressStatus);
4498        I::map_ip_in(
4499            Wrap(status),
4500            |Wrap(v4_status)| match v4_status {
4501                Ipv4PresentAddressStatus::UnicastTentative => 0,
4502                _ => 1,
4503            },
4504            |Wrap(v6_status)| match v6_status {
4505                Ipv6PresentAddressStatus::UnicastTentative => 0,
4506                _ => 1,
4507            },
4508        )
4509    })
4510}
4511
4512/// The reason a received IP packet is dropped.
4513#[derive(Debug, PartialEq)]
4514pub enum DropReason {
4515    /// Remote packet destined to tentative address.
4516    Tentative,
4517    /// Remote packet destined to the unspecified address.
4518    UnspecifiedDestination,
4519    /// Remote packet with an invalid destination address.
4520    InvalidDestination,
4521    /// Cannot forward a packet with unspecified source address.
4522    ForwardUnspecifiedSource,
4523    /// Cannot forward a packet with link-local source or destination address.
4524    ForwardLinkLocal,
4525    /// Packet should be forwarded but packet's inbound interface has forwarding
4526    /// disabled.
4527    ForwardingDisabledInboundIface,
4528    /// Remote packet destined to a multicast address that could not be:
4529    /// * delivered locally (because we are not a member of the multicast
4530    ///   group), or
4531    /// * forwarded (either because multicast forwarding is disabled, or no
4532    ///   applicable multicast route has been installed).
4533    MulticastNoInterest,
4534}
4535
4536/// Computes the action to take in order to process a received IPv4 packet.
4537pub fn receive_ipv4_packet_action<BC, CC, B>(
4538    core_ctx: &mut CC,
4539    bindings_ctx: &mut BC,
4540    device: &CC::DeviceId,
4541    packet: &Ipv4Packet<B>,
4542    frame_dst: Option<LocalFrameDestination>,
4543    marks: &Marks,
4544    max_fragment_len: Option<usize>,
4545) -> ReceivePacketAction<Ipv4, CC::DeviceId>
4546where
4547    BC: IpLayerBindingsContext<Ipv4, CC::DeviceId>,
4548    CC: IpLayerContext<Ipv4, BC>,
4549    B: SplitByteSlice,
4550{
4551    let Some(dst_ip) = SpecifiedAddr::new(packet.dst_ip()) else {
4552        core_ctx.increment_both(device, |c| &c.unspecified_destination);
4553        return ReceivePacketAction::Drop { reason: DropReason::UnspecifiedDestination };
4554    };
4555
4556    // If the packet arrived at the loopback interface, check if any local
4557    // interface has the destination address assigned. This effectively lets the
4558    // loopback interface operate as a weak host for incoming packets.
4559    //
4560    // Note that (as of writing) the stack sends all locally destined traffic to
4561    // the loopback interface so we need this hack to allow the stack to accept
4562    // packets that arrive at the loopback interface (after being looped back)
4563    // but destined to an address that is assigned to another local interface.
4564    //
4565    // TODO(https://fxbug.dev/42065870): This should instead be controlled by
4566    // the routing table.
4567
4568    let highest_priority = if device.is_loopback() {
4569        core_ctx.with_address_statuses(dst_ip, |it| {
4570            let it = it.map(|(_device, status)| status);
4571            choose_highest_priority_address_status::<Ipv4>(it)
4572        })
4573    } else {
4574        core_ctx.address_status_for_device(dst_ip, device).into_present()
4575    };
4576    match highest_priority {
4577        Some(
4578            address_status @ (Ipv4PresentAddressStatus::UnicastAssigned
4579            | Ipv4PresentAddressStatus::LoopbackSubnet),
4580        ) => {
4581            core_ctx.increment_both(device, |c| &c.deliver_unicast);
4582            ReceivePacketAction::Deliver {
4583                address_status,
4584                internal_forwarding: InternalForwarding::NotUsed,
4585            }
4586        }
4587        Some(Ipv4PresentAddressStatus::UnicastTentative) => {
4588            // If the destination address is tentative (which implies that
4589            // we are still performing Duplicate Address Detection on
4590            // it), then we don't consider the address "assigned to an
4591            // interface", and so we drop packets instead of delivering them
4592            // locally.
4593            core_ctx.increment_both(device, |c| &c.drop_for_tentative);
4594            ReceivePacketAction::Drop { reason: DropReason::Tentative }
4595        }
4596
4597        Some(address_status @ Ipv4PresentAddressStatus::Multicast) => {
4598            receive_ip_multicast_packet_action(
4599                core_ctx,
4600                bindings_ctx,
4601                device,
4602                packet,
4603                Some(address_status),
4604                dst_ip,
4605                frame_dst,
4606                max_fragment_len,
4607            )
4608        }
4609        Some(
4610            address_status @ (Ipv4PresentAddressStatus::LimitedBroadcast
4611            | Ipv4PresentAddressStatus::SubnetBroadcast),
4612        ) => {
4613            core_ctx.increment_both(device, |c| &c.version_rx.deliver_broadcast);
4614            ReceivePacketAction::Deliver {
4615                address_status,
4616                internal_forwarding: InternalForwarding::NotUsed,
4617            }
4618        }
4619        None => receive_ip_packet_action_common::<Ipv4, _, _, _>(
4620            core_ctx,
4621            bindings_ctx,
4622            dst_ip,
4623            device,
4624            packet,
4625            frame_dst,
4626            marks,
4627            max_fragment_len,
4628        ),
4629    }
4630}
4631
4632/// Computes the action to take in order to process a received IPv6 packet.
4633pub fn receive_ipv6_packet_action<BC, CC, B>(
4634    core_ctx: &mut CC,
4635    bindings_ctx: &mut BC,
4636    device: &CC::DeviceId,
4637    packet: &Ipv6Packet<B>,
4638    frame_dst: Option<LocalFrameDestination>,
4639    marks: &Marks,
4640    max_fragment_len: Option<usize>,
4641) -> ReceivePacketAction<Ipv6, CC::DeviceId>
4642where
4643    BC: IpLayerBindingsContext<Ipv6, CC::DeviceId>,
4644    CC: IpLayerContext<Ipv6, BC>,
4645    B: SplitByteSlice,
4646{
4647    let Some(dst_ip) = SpecifiedAddr::new(packet.dst_ip()) else {
4648        core_ctx.increment_both(device, |c| &c.unspecified_destination);
4649        return ReceivePacketAction::Drop { reason: DropReason::UnspecifiedDestination };
4650    };
4651
4652    // If the packet arrived at the loopback interface, check if any local
4653    // interface has the destination address assigned. This effectively lets
4654    // the loopback interface operate as a weak host for incoming packets.
4655    //
4656    // Note that (as of writing) the stack sends all locally destined traffic to
4657    // the loopback interface so we need this hack to allow the stack to accept
4658    // packets that arrive at the loopback interface (after being looped back)
4659    // but destined to an address that is assigned to another local interface.
4660    //
4661    // TODO(https://fxbug.dev/42175703): This should instead be controlled by the
4662    // routing table.
4663
4664    let highest_priority = if device.is_loopback() {
4665        core_ctx.with_address_statuses(dst_ip, |it| {
4666            let it = it.map(|(_device, status)| status);
4667            choose_highest_priority_address_status::<Ipv6>(it)
4668        })
4669    } else {
4670        core_ctx.address_status_for_device(dst_ip, device).into_present()
4671    };
4672    match highest_priority {
4673        Some(address_status @ Ipv6PresentAddressStatus::Multicast) => {
4674            receive_ip_multicast_packet_action(
4675                core_ctx,
4676                bindings_ctx,
4677                device,
4678                packet,
4679                Some(address_status),
4680                dst_ip,
4681                frame_dst,
4682                max_fragment_len,
4683            )
4684        }
4685        Some(address_status @ Ipv6PresentAddressStatus::UnicastAssigned) => {
4686            core_ctx.increment_both(device, |c| &c.deliver_unicast);
4687            ReceivePacketAction::Deliver {
4688                address_status,
4689                internal_forwarding: InternalForwarding::NotUsed,
4690            }
4691        }
4692        Some(Ipv6PresentAddressStatus::UnicastTentative) => {
4693            // If the destination address is tentative (which implies that
4694            // we are still performing NDP's Duplicate Address Detection on
4695            // it), then we don't consider the address "assigned to an
4696            // interface", and so we drop packets instead of delivering them
4697            // locally.
4698            //
4699            // As per RFC 4862 section 5.4:
4700            //
4701            //   An address on which the Duplicate Address Detection
4702            //   procedure is applied is said to be tentative until the
4703            //   procedure has completed successfully. A tentative address
4704            //   is not considered "assigned to an interface" in the
4705            //   traditional sense.  That is, the interface must accept
4706            //   Neighbor Solicitation and Advertisement messages containing
4707            //   the tentative address in the Target Address field, but
4708            //   processes such packets differently from those whose Target
4709            //   Address matches an address assigned to the interface. Other
4710            //   packets addressed to the tentative address should be
4711            //   silently discarded. Note that the "other packets" include
4712            //   Neighbor Solicitation and Advertisement messages that have
4713            //   the tentative (i.e., unicast) address as the IP destination
4714            //   address and contain the tentative address in the Target
4715            //   Address field.  Such a case should not happen in normal
4716            //   operation, though, since these messages are multicasted in
4717            //   the Duplicate Address Detection procedure.
4718            //
4719            // That is, we accept no packets destined to a tentative
4720            // address. NS and NA packets should be addressed to a multicast
4721            // address that we would have joined during DAD so that we can
4722            // receive those packets.
4723            core_ctx.increment_both(device, |c| &c.drop_for_tentative);
4724            ReceivePacketAction::Drop { reason: DropReason::Tentative }
4725        }
4726        None => receive_ip_packet_action_common::<Ipv6, _, _, _>(
4727            core_ctx,
4728            bindings_ctx,
4729            dst_ip,
4730            device,
4731            packet,
4732            frame_dst,
4733            marks,
4734            max_fragment_len,
4735        ),
4736    }
4737}
4738
4739/// Computes the action to take for multicast packets on behalf of
4740/// [`receive_ipv4_packet_action`] and [`receive_ipv6_packet_action`].
4741fn receive_ip_multicast_packet_action<
4742    I: IpLayerIpExt,
4743    B: SplitByteSlice,
4744    BC: IpLayerBindingsContext<I, CC::DeviceId>,
4745    CC: IpLayerContext<I, BC>,
4746>(
4747    core_ctx: &mut CC,
4748    bindings_ctx: &mut BC,
4749    device: &CC::DeviceId,
4750    packet: &I::Packet<B>,
4751    address_status: Option<I::AddressStatus>,
4752    dst_ip: SpecifiedAddr<I::Addr>,
4753    frame_dst: Option<LocalFrameDestination>,
4754    max_fragment_len: Option<usize>,
4755) -> ReceivePacketAction<I, CC::DeviceId> {
4756    let targets = multicast_forwarding::lookup_multicast_route_or_stash_packet(
4757        core_ctx,
4758        bindings_ctx,
4759        packet,
4760        device,
4761        frame_dst,
4762        max_fragment_len,
4763    );
4764    match (targets, address_status) {
4765        (Some(targets), address_status) => {
4766            if address_status.is_some() {
4767                core_ctx.increment_both(device, |c| &c.deliver_multicast);
4768            }
4769            ReceivePacketAction::MulticastForward { targets, address_status, dst_ip }
4770        }
4771        (None, Some(address_status)) => {
4772            // If the address was present on the device (e.g. the host is a
4773            // member of the multicast group), fallback to local delivery.
4774            core_ctx.increment_both(device, |c| &c.deliver_multicast);
4775            ReceivePacketAction::Deliver {
4776                address_status,
4777                internal_forwarding: InternalForwarding::NotUsed,
4778            }
4779        }
4780        (None, None) => {
4781            // As per RFC 1122 Section 3.2.2
4782            //   An ICMP error message MUST NOT be sent as the result of
4783            //   receiving:
4784            //   ...
4785            //   * a datagram destined to an IP broadcast or IP multicast
4786            //     address
4787            //
4788            // As such, drop the packet
4789            core_ctx.increment_both(device, |c| &c.multicast_no_interest);
4790            ReceivePacketAction::Drop { reason: DropReason::MulticastNoInterest }
4791        }
4792    }
4793}
4794
4795/// Computes the remaining protocol-agnostic actions on behalf of
4796/// [`receive_ipv4_packet_action`] and [`receive_ipv6_packet_action`].
4797fn receive_ip_packet_action_common<
4798    I: IpLayerIpExt,
4799    B: SplitByteSlice,
4800    BC: IpLayerBindingsContext<I, CC::DeviceId>,
4801    CC: IpLayerContext<I, BC>,
4802>(
4803    core_ctx: &mut CC,
4804    bindings_ctx: &mut BC,
4805    dst_ip: SpecifiedAddr<I::Addr>,
4806    device_id: &CC::DeviceId,
4807    packet: &I::Packet<B>,
4808    frame_dst: Option<LocalFrameDestination>,
4809    marks: &Marks,
4810    max_fragment_len: Option<usize>,
4811) -> ReceivePacketAction<I, CC::DeviceId> {
4812    if dst_ip.is_multicast() {
4813        return receive_ip_multicast_packet_action(
4814            core_ctx,
4815            bindings_ctx,
4816            device_id,
4817            packet,
4818            None,
4819            dst_ip,
4820            frame_dst,
4821            max_fragment_len,
4822        );
4823    }
4824
4825    // Don't allow mapped IPv6 addresses.
4826    let Some(dst_ip) = NonMappedAddr::new(dst_ip) else {
4827        return ReceivePacketAction::Drop { reason: DropReason::InvalidDestination };
4828    };
4829
4830    // The packet is not destined locally, so we attempt to forward it.
4831    if !core_ctx.is_device_unicast_forwarding_enabled(device_id) {
4832        // Forwarding is disabled; we are operating only as a host.
4833        //
4834        // For IPv4, per RFC 1122 Section 3.2.1.3, "A host MUST silently discard
4835        // an incoming datagram that is not destined for the host."
4836        //
4837        // For IPv6, per RFC 4443 Section 3.1, the only instance in which a host
4838        // sends an ICMPv6 Destination Unreachable message is when a packet is
4839        // destined to that host but on an unreachable port (Code 4 - "Port
4840        // unreachable"). Since the only sensible error message to send in this
4841        // case is a Destination Unreachable message, we interpret the RFC text
4842        // to mean that, consistent with IPv4's behavior, we should silently
4843        // discard the packet in this case.
4844        core_ctx.increment_both(device_id, |c| &c.forwarding_disabled);
4845        return ReceivePacketAction::Drop { reason: DropReason::ForwardingDisabledInboundIface };
4846    }
4847    // Per https://www.rfc-editor.org/rfc/rfc4291.html#section-2.5.2:
4848    //   An IPv6 packet with a source address of unspecified must never be forwarded by an IPv6
4849    //   router.
4850    // Per https://datatracker.ietf.org/doc/html/rfc1812#section-5.3.7:
4851    //   A router SHOULD NOT forward any packet that has an invalid IP source address or a source
4852    //   address on network 0
4853    let Some(source_address) = SpecifiedAddr::new(packet.src_ip()) else {
4854        return ReceivePacketAction::Drop { reason: DropReason::ForwardUnspecifiedSource };
4855    };
4856
4857    // If forwarding is enabled, allow local delivery if the packet is destined
4858    // for an IP assigned to a different interface.
4859    //
4860    // This enables a weak host model when the Netstack is configured as a
4861    // router. Conceptually, the netstack is forwarding the packet from the
4862    // input device, to the destination IP's device.
4863    if let Some(dst_ip) = NonMulticastAddr::new(dst_ip) {
4864        if let Some((outbound_device, address_status)) =
4865            get_device_with_assigned_address(core_ctx, IpDeviceAddr::new_from_witness(dst_ip))
4866        {
4867            return ReceivePacketAction::Deliver {
4868                address_status,
4869                internal_forwarding: InternalForwarding::Used(outbound_device),
4870            };
4871        }
4872    }
4873
4874    // For IPv4, RFC 3927 Section 2.7 states:
4875    //
4876    //   An IPv4 packet whose source and/or destination address is in the
4877    //   169.254/16 prefix MUST NOT be sent to any router for forwarding, and
4878    //   any network device receiving such a packet MUST NOT forward it,
4879    //   regardless of the TTL in the IPv4 header.
4880    //
4881    // However, to maintain behavioral similarity to both gVisor/Netstack2 and
4882    // Linux, we omit this check.
4883    //
4884    // For IPv6, RFC 4291 Section 2.5.6 states:
4885    //
4886    //   Routers must not forward any packets with Link-Local source or
4887    //   destination addresses to other links.
4888    if I::map_ip_in(
4889        &packet,
4890        |_| false,
4891        |packet| packet.src_ip().is_link_local() || packet.dst_ip().is_link_local(),
4892    ) {
4893        return ReceivePacketAction::Drop { reason: DropReason::ForwardLinkLocal };
4894    }
4895
4896    match lookup_route_table(
4897        core_ctx,
4898        dst_ip.get(),
4899        RuleInput {
4900            packet_origin: PacketOrigin::NonLocal { source_address, incoming_device: device_id },
4901            marks,
4902        },
4903    ) {
4904        Some(dst) => {
4905            core_ctx.increment_both(device_id, |c| &c.forward);
4906            ReceivePacketAction::Forward { original_dst: *dst_ip, dst }
4907        }
4908        None => {
4909            core_ctx.increment_both(device_id, |c| &c.no_route_to_host);
4910            ReceivePacketAction::SendNoRouteToDest { dst: dst_ip }
4911        }
4912    }
4913}
4914
4915// Look up the route to a host.
4916fn lookup_route_table<
4917    I: IpLayerIpExt,
4918    BC: IpLayerBindingsContext<I, CC::DeviceId>,
4919    CC: IpStateContext<I, BC>,
4920>(
4921    core_ctx: &mut CC,
4922    dst_ip: I::Addr,
4923    rule_input: RuleInput<'_, I, CC::DeviceId>,
4924) -> Option<Destination<I::Addr, CC::DeviceId>> {
4925    let bound_device = match rule_input.packet_origin {
4926        PacketOrigin::Local { bound_address: _, bound_device } => bound_device,
4927        PacketOrigin::NonLocal { source_address: _, incoming_device: _ } => None,
4928    };
4929    core_ctx.with_rules_table(|core_ctx, rules: &RulesTable<_, _, BC>| {
4930        match walk_rules(core_ctx, rules, (), &rule_input, |(), core_ctx, table| {
4931            match table.lookup(core_ctx, bound_device, dst_ip) {
4932                Some(dst) => ControlFlow::Break(Some(dst)),
4933                None => ControlFlow::Continue(()),
4934            }
4935        }) {
4936            ControlFlow::Break(RuleAction::Lookup(RuleWalkInfo {
4937                inner: dst,
4938                observed_source_address_matcher: _,
4939            })) => dst,
4940            ControlFlow::Break(RuleAction::Unreachable) => None,
4941            ControlFlow::Continue(RuleWalkInfo {
4942                inner: (),
4943                observed_source_address_matcher: _,
4944            }) => None,
4945        }
4946    })
4947}
4948
4949/// Packed destination passed to [`IpDeviceSendContext::send_ip_frame`].
4950#[derive(Debug, Derivative, Clone)]
4951#[derivative(Eq(bound = "D: Eq"), PartialEq(bound = "D: PartialEq"))]
4952pub enum IpPacketDestination<I: BroadcastIpExt, D> {
4953    /// Broadcast packet.
4954    Broadcast(I::BroadcastMarker),
4955
4956    /// Multicast packet to the specified IP.
4957    Multicast(MulticastAddr<I::Addr>),
4958
4959    /// Send packet to the neighbor with the specified IP (the receiving
4960    /// node is either a router or the final recipient of the packet).
4961    Neighbor(SpecifiedAddr<I::Addr>),
4962
4963    /// Loopback the packet to the specified device. Can be used only when
4964    /// sending to the loopback device.
4965    Loopback(D),
4966}
4967
4968impl<I: BroadcastIpExt, D> IpPacketDestination<I, D> {
4969    /// Creates `IpPacketDestination` for IP address.
4970    pub fn from_addr(addr: SpecifiedAddr<I::Addr>) -> Self {
4971        match MulticastAddr::new(addr.into_addr()) {
4972            Some(mc_addr) => Self::Multicast(mc_addr),
4973            None => Self::Neighbor(addr),
4974        }
4975    }
4976
4977    /// Create `IpPacketDestination` from `NextHop`.
4978    pub fn from_next_hop(next_hop: NextHop<I::Addr>, dst_ip: SpecifiedAddr<I::Addr>) -> Self {
4979        match next_hop {
4980            NextHop::RemoteAsNeighbor => Self::from_addr(dst_ip),
4981            NextHop::Gateway(gateway) => Self::Neighbor(gateway),
4982            NextHop::Broadcast(marker) => Self::Broadcast(marker),
4983        }
4984    }
4985}
4986
4987/// The metadata associated with an outgoing IP packet.
4988#[derive(Debug, Clone)]
4989pub struct SendIpPacketMeta<I: IpExt, D, Src> {
4990    /// The outgoing device.
4991    pub device: D,
4992
4993    /// The source address of the packet.
4994    pub src_ip: Src,
4995
4996    /// The destination address of the packet.
4997    pub dst_ip: SpecifiedAddr<I::Addr>,
4998
4999    /// The destination for the send operation.
5000    pub destination: IpPacketDestination<I, D>,
5001
5002    /// The upper-layer protocol held in the packet's payload.
5003    pub proto: I::Proto,
5004
5005    /// The time-to-live (IPv4) or hop limit (IPv6) for the packet.
5006    ///
5007    /// If not set, a default TTL may be used.
5008    pub ttl: Option<NonZeroU8>,
5009
5010    /// An MTU to artificially impose on the whole IP packet.
5011    ///
5012    /// Note that the device's and discovered path MTU may still be imposed on
5013    /// the packet.
5014    pub mtu: Mtu,
5015
5016    /// Traffic Class (IPv6) or Type of Service (IPv4) field for the packet.
5017    pub dscp_and_ecn: DscpAndEcn,
5018}
5019
5020impl<I: IpExt, D> From<SendIpPacketMeta<I, D, SpecifiedAddr<I::Addr>>>
5021    for SendIpPacketMeta<I, D, Option<SpecifiedAddr<I::Addr>>>
5022{
5023    fn from(
5024        SendIpPacketMeta { device, src_ip, dst_ip, destination, proto, ttl, mtu, dscp_and_ecn }: SendIpPacketMeta<
5025            I,
5026            D,
5027            SpecifiedAddr<I::Addr>,
5028        >,
5029    ) -> SendIpPacketMeta<I, D, Option<SpecifiedAddr<I::Addr>>> {
5030        SendIpPacketMeta {
5031            device,
5032            src_ip: Some(src_ip),
5033            dst_ip,
5034            destination,
5035            proto,
5036            ttl,
5037            mtu,
5038            dscp_and_ecn,
5039        }
5040    }
5041}
5042
5043/// Trait for abstracting the IP layer for locally-generated traffic.  That is,
5044/// traffic generated by the netstack itself (e.g. ICMP, IGMP, or MLD).
5045///
5046/// NOTE: Due to filtering rules, it is possible that the device provided in
5047/// `meta` will not be the device that final IP packet is actually sent from.
5048pub trait IpLayerHandler<I: IpExt + FragmentationIpExt + FilterIpExt, BC>:
5049    DeviceIdContext<AnyDevice>
5050{
5051    /// Encapsulate and send the provided transport packet and from the device
5052    /// provided in `meta`.
5053    fn send_ip_packet_from_device<S>(
5054        &mut self,
5055        bindings_ctx: &mut BC,
5056        meta: SendIpPacketMeta<I, &Self::DeviceId, Option<SpecifiedAddr<I::Addr>>>,
5057        body: S,
5058    ) -> Result<(), IpSendFrameError<S>>
5059    where
5060        S: TransportPacketSerializer<I>,
5061        S::Buffer: BufferMut;
5062
5063    /// Send an IP packet that doesn't require the encapsulation and other
5064    /// processing of [`send_ip_packet_from_device`] from the device specified
5065    /// in `meta`.
5066    // TODO(https://fxbug.dev/333908066): The packets going through this
5067    // function only hit the EGRESS filter hook, bypassing LOCAL_EGRESS.
5068    // Refactor callers and other functions to prevent this.
5069    fn send_ip_frame<S>(
5070        &mut self,
5071        bindings_ctx: &mut BC,
5072        device: &Self::DeviceId,
5073        destination: IpPacketDestination<I, &Self::DeviceId>,
5074        body: S,
5075    ) -> Result<(), IpSendFrameError<S>>
5076    where
5077        S: FragmentableIpSerializer<I, Buffer: BufferMut> + FilterIpPacket<I>;
5078}
5079
5080impl<
5081    I: IpLayerIpExt,
5082    BC: IpLayerBindingsContext<I, <CC as DeviceIdContext<AnyDevice>>::DeviceId>,
5083    CC: IpLayerEgressContext<I, BC> + IpDeviceEgressStateContext<I> + IpDeviceMtuContext<I>,
5084> IpLayerHandler<I, BC> for CC
5085{
5086    fn send_ip_packet_from_device<S>(
5087        &mut self,
5088        bindings_ctx: &mut BC,
5089        meta: SendIpPacketMeta<I, &CC::DeviceId, Option<SpecifiedAddr<I::Addr>>>,
5090        body: S,
5091    ) -> Result<(), IpSendFrameError<S>>
5092    where
5093        S: TransportPacketSerializer<I>,
5094        S::Buffer: BufferMut,
5095    {
5096        send_ip_packet_from_device(self, bindings_ctx, meta, body, IpLayerPacketMetadata::default())
5097    }
5098
5099    fn send_ip_frame<S>(
5100        &mut self,
5101        bindings_ctx: &mut BC,
5102        device: &Self::DeviceId,
5103        destination: IpPacketDestination<I, &Self::DeviceId>,
5104        body: S,
5105    ) -> Result<(), IpSendFrameError<S>>
5106    where
5107        S: FragmentableIpSerializer<I, Buffer: BufferMut> + FilterIpPacket<I>,
5108    {
5109        send_ip_frame(
5110            self,
5111            bindings_ctx,
5112            device,
5113            destination,
5114            body,
5115            IpLayerPacketMetadata::default(),
5116            Mtu::no_limit(),
5117        )
5118    }
5119}
5120
5121/// Sends an Ip packet with the specified metadata.
5122///
5123/// # Panics
5124///
5125/// Panics if either the source or destination address is the loopback address
5126/// and the device is a non-loopback device.
5127pub(crate) fn send_ip_packet_from_device<I, BC, CC, S>(
5128    core_ctx: &mut CC,
5129    bindings_ctx: &mut BC,
5130    meta: SendIpPacketMeta<
5131        I,
5132        &<CC as DeviceIdContext<AnyDevice>>::DeviceId,
5133        Option<SpecifiedAddr<I::Addr>>,
5134    >,
5135    body: S,
5136    packet_metadata: IpLayerPacketMetadata<I, CC::WeakAddressId, BC>,
5137) -> Result<(), IpSendFrameError<S>>
5138where
5139    I: IpLayerIpExt,
5140    BC: FilterBindingsContext<CC::DeviceId> + TxMetadataBindingsTypes + MarksBindingsContext,
5141    CC: IpLayerEgressContext<I, BC> + IpDeviceEgressStateContext<I> + IpDeviceMtuContext<I>,
5142    S: TransportPacketSerializer<I>,
5143    S::Buffer: BufferMut,
5144{
5145    let SendIpPacketMeta { device, src_ip, dst_ip, destination, proto, ttl, mtu, dscp_and_ecn } =
5146        meta;
5147    core_ctx.increment_both(device, |c| &c.send_ip_packet);
5148    let next_packet_id = gen_ip_packet_id(core_ctx);
5149    let ttl = ttl.unwrap_or_else(|| core_ctx.get_hop_limit(device)).get();
5150    let src_ip = src_ip.map_or(I::UNSPECIFIED_ADDRESS, |a| a.get());
5151    let mut builder = I::PacketBuilder::new(src_ip, dst_ip.get(), ttl, proto);
5152
5153    #[derive(GenericOverIp)]
5154    #[generic_over_ip(I, Ip)]
5155    struct Wrap<'a, I: IpLayerIpExt> {
5156        builder: &'a mut I::PacketBuilder<NetworkSerializationContext>,
5157        next_packet_id: I::PacketId,
5158    }
5159
5160    I::map_ip::<_, ()>(
5161        Wrap { builder: &mut builder, next_packet_id },
5162        |Wrap { builder, next_packet_id }| {
5163            builder.id(next_packet_id);
5164        },
5165        |Wrap { builder: _, next_packet_id: () }| {
5166            // IPv6 doesn't have packet IDs.
5167        },
5168    );
5169
5170    builder.set_dscp_and_ecn(dscp_and_ecn);
5171
5172    let ip_frame = builder.wrap_body(body);
5173    send_ip_frame(core_ctx, bindings_ctx, device, destination, ip_frame, packet_metadata, mtu)
5174        .map_err(|ser| ser.map_serializer(|s| s.into_inner()))
5175}
5176
5177/// Abstracts access to a [`filter::FilterHandler`] for core contexts.
5178pub trait FilterHandlerProvider<I: FilterIpExt, BT: FilterBindingsTypes>:
5179    IpDeviceAddressIdContext<I, DeviceId: netstack3_base::InterfaceProperties<BT::DeviceClass>>
5180{
5181    /// The filter handler.
5182    type Handler<'a>: filter::FilterHandler<I, BT, DeviceId = Self::DeviceId, WeakAddressId = Self::WeakAddressId>
5183    where
5184        Self: 'a;
5185
5186    /// Gets the filter handler for this context.
5187    fn filter_handler(&mut self) -> Self::Handler<'_>;
5188}
5189
5190#[cfg(any(test, feature = "testutils"))]
5191pub(crate) mod testutil {
5192    use super::*;
5193
5194    use netstack3_base::testutil::{FakeBindingsCtx, FakeCoreCtx, FakeStrongDeviceId};
5195    use netstack3_base::{
5196        AssignedAddrIpExt, NetworkSerializer, SendFrameContext, SendFrameError, SendableFrameMeta,
5197    };
5198
5199    /// A [`SendIpPacketMeta`] for dual stack contextx.
5200    #[derive(Debug, GenericOverIp)]
5201    #[generic_over_ip()]
5202    #[allow(missing_docs)]
5203    pub enum DualStackSendIpPacketMeta<D> {
5204        V4(SendIpPacketMeta<Ipv4, D, SpecifiedAddr<Ipv4Addr>>),
5205        V6(SendIpPacketMeta<Ipv6, D, SpecifiedAddr<Ipv6Addr>>),
5206    }
5207
5208    impl<I: IpExt, D> From<SendIpPacketMeta<I, D, SpecifiedAddr<I::Addr>>>
5209        for DualStackSendIpPacketMeta<D>
5210    {
5211        fn from(value: SendIpPacketMeta<I, D, SpecifiedAddr<I::Addr>>) -> Self {
5212            #[derive(GenericOverIp)]
5213            #[generic_over_ip(I, Ip)]
5214            struct Wrap<I: IpExt, D>(SendIpPacketMeta<I, D, SpecifiedAddr<I::Addr>>);
5215            use DualStackSendIpPacketMeta::*;
5216            I::map_ip_in(Wrap(value), |Wrap(value)| V4(value), |Wrap(value)| V6(value))
5217        }
5218    }
5219
5220    impl<I: IpExt, S, DeviceId, BC>
5221        SendableFrameMeta<FakeCoreCtx<S, DualStackSendIpPacketMeta<DeviceId>, DeviceId>, BC>
5222        for SendIpPacketMeta<I, DeviceId, SpecifiedAddr<I::Addr>>
5223    {
5224        fn send_meta<SS>(
5225            self,
5226            core_ctx: &mut FakeCoreCtx<S, DualStackSendIpPacketMeta<DeviceId>, DeviceId>,
5227            bindings_ctx: &mut BC,
5228            frame: SS,
5229        ) -> Result<(), SendFrameError<SS>>
5230        where
5231            SS: NetworkSerializer,
5232            SS::Buffer: BufferMut,
5233        {
5234            SendFrameContext::send_frame(
5235                &mut core_ctx.frames,
5236                bindings_ctx,
5237                DualStackSendIpPacketMeta::from(self),
5238                frame,
5239            )
5240        }
5241    }
5242
5243    /// Error returned when the IP version doesn't match.
5244    #[derive(Debug)]
5245    pub struct WrongIpVersion;
5246
5247    impl<D> DualStackSendIpPacketMeta<D> {
5248        /// Returns the internal [`SendIpPacketMeta`] if this is carrying the
5249        /// version matching `I`.
5250        pub fn try_as<I: IpExt>(
5251            &self,
5252        ) -> Result<&SendIpPacketMeta<I, D, SpecifiedAddr<I::Addr>>, WrongIpVersion> {
5253            #[derive(GenericOverIp)]
5254            #[generic_over_ip(I, Ip)]
5255            struct Wrap<'a, I: IpExt, D>(
5256                Option<&'a SendIpPacketMeta<I, D, SpecifiedAddr<I::Addr>>>,
5257            );
5258            use DualStackSendIpPacketMeta::*;
5259            let Wrap(dual_stack) = I::map_ip(
5260                self,
5261                |value| {
5262                    Wrap(match value {
5263                        V4(meta) => Some(meta),
5264                        V6(_) => None,
5265                    })
5266                },
5267                |value| {
5268                    Wrap(match value {
5269                        V4(_) => None,
5270                        V6(meta) => Some(meta),
5271                    })
5272                },
5273            );
5274            dual_stack.ok_or(WrongIpVersion)
5275        }
5276    }
5277
5278    impl<I, BC, S, Meta, DeviceId> FilterHandlerProvider<I, BC> for FakeCoreCtx<S, Meta, DeviceId>
5279    where
5280        I: AssignedAddrIpExt + FilterIpExt,
5281        BC: FilterBindingsContext<DeviceId>,
5282        DeviceId: FakeStrongDeviceId + netstack3_base::InterfaceProperties<BC::DeviceClass>,
5283    {
5284        type Handler<'a>
5285            = filter::testutil::NoopImpl<DeviceId>
5286        where
5287            Self: 'a;
5288
5289        fn filter_handler(&mut self) -> Self::Handler<'_> {
5290            filter::testutil::NoopImpl::default()
5291        }
5292    }
5293
5294    impl<TimerId, Event: Debug, State, FrameMeta> MarksBindingsContext
5295        for FakeBindingsCtx<TimerId, Event, State, FrameMeta>
5296    {
5297        fn marks_to_keep_on_egress() -> &'static [MarkDomain] {
5298            const MARKS: [MarkDomain; 1] = [MarkDomain::Mark1];
5299            &MARKS
5300        }
5301
5302        fn marks_to_set_on_ingress() -> &'static [MarkDomain] {
5303            const MARKS: [MarkDomain; 1] = [MarkDomain::Mark2];
5304            &MARKS
5305        }
5306    }
5307}
5308
5309#[cfg(test)]
5310mod test {
5311    use super::*;
5312
5313    #[test]
5314    fn highest_priority_address_status_v4() {
5315        // Prefer assigned addresses over tentative addresses.
5316        assert_eq!(
5317            choose_highest_priority_address_status::<Ipv4>(
5318                [
5319                    Ipv4PresentAddressStatus::UnicastAssigned,
5320                    Ipv4PresentAddressStatus::UnicastTentative
5321                ]
5322                .into_iter()
5323            ),
5324            Some(Ipv4PresentAddressStatus::UnicastAssigned)
5325        )
5326    }
5327
5328    #[test]
5329    fn highest_priority_address_status_v6() {
5330        // Prefer assigned addresses over tentative addresses.
5331        assert_eq!(
5332            choose_highest_priority_address_status::<Ipv6>(
5333                [
5334                    Ipv6PresentAddressStatus::UnicastAssigned,
5335                    Ipv6PresentAddressStatus::UnicastTentative
5336                ]
5337                .into_iter()
5338            ),
5339            Some(Ipv6PresentAddressStatus::UnicastAssigned)
5340        )
5341    }
5342}