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