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