netstack3_ip/
base.rs

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