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