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