netstack3_ip/
icmp.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
5//! The Internet Control Message Protocol (ICMP).
6
7use alloc::boxed::Box;
8use core::convert::TryInto as _;
9use core::num::{NonZeroU8, NonZeroU16};
10
11use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
12use log::{debug, error, trace};
13use net_types::ip::{
14    GenericOverIp, Ip, IpAddress, IpMarked, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr,
15    Ipv6SourceAddr, Mtu, SubnetError,
16};
17use net_types::{
18    LinkLocalAddress, LinkLocalUnicastAddr, MulticastAddress, NonMulticastAddr, SpecifiedAddr,
19    UnicastAddr, Witness,
20};
21use netstack3_base::socket::{AddrIsMappedError, SocketIpAddr, SocketIpAddrExt as _};
22use netstack3_base::sync::Mutex;
23use netstack3_base::{
24    AnyDevice, Counter, CounterContext, DeviceIdContext, EitherDeviceId, FrameDestination,
25    IcmpIpExt, Icmpv4ErrorCode, Icmpv6ErrorCode, InstantBindingsTypes, InstantContext,
26    IpDeviceAddr, IpExt, Marks, RngContext, TokenBucket, TxMetadataBindingsTypes,
27};
28use netstack3_filter::{DynTransportSerializer, DynamicTransportSerializer, FilterIpExt};
29use packet::{
30    BufferMut, InnerPacketBuilder as _, PacketBuilder as _, ParsablePacket as _, ParseBuffer,
31    PartialSerializer, Serializer, TruncateDirection, TruncatingSerializer,
32};
33use packet_formats::icmp::ndp::options::{NdpOption, NdpOptionBuilder};
34use packet_formats::icmp::ndp::{
35    NdpPacket, NeighborAdvertisement, NeighborSolicitation, NonZeroNdpLifetime,
36    OptionSequenceBuilder, RouterSolicitation,
37};
38use packet_formats::icmp::{
39    IcmpDestUnreachable, IcmpEchoRequest, IcmpMessage, IcmpMessageType, IcmpPacket,
40    IcmpPacketBuilder, IcmpPacketRaw, IcmpParseArgs, IcmpTimeExceeded, IcmpZeroCode,
41    Icmpv4DestUnreachableCode, Icmpv4Packet, Icmpv4ParameterProblem, Icmpv4ParameterProblemCode,
42    Icmpv4TimeExceededCode, Icmpv6DestUnreachableCode, Icmpv6Packet, Icmpv6PacketTooBig,
43    Icmpv6ParameterProblem, Icmpv6ParameterProblemCode, Icmpv6TimeExceededCode, MessageBody,
44    OriginalPacket, peek_message_type,
45};
46use packet_formats::ip::{DscpAndEcn, IpPacket, Ipv4Proto, Ipv6Proto};
47use packet_formats::ipv4::{Ipv4FragmentType, Ipv4Header, Ipv4OnlyMeta};
48use packet_formats::ipv6::{ExtHdrParseError, Ipv6Header};
49use zerocopy::SplitByteSlice;
50
51use crate::internal::base::{
52    AddressStatus, IPV6_DEFAULT_SUBNET, IpDeviceIngressStateContext, IpLayerHandler,
53    IpPacketDestination, IpSendFrameError, IpTransportContext, Ipv6PresentAddressStatus,
54    NdpBindingsContext, RouterAdvertisementEvent, SendIpPacketMeta, TransportReceiveError,
55};
56use crate::internal::device::nud::{ConfirmationFlags, NudIpHandler};
57use crate::internal::device::route_discovery::Ipv6DiscoveredRoute;
58use crate::internal::device::{
59    IpAddressState, IpDeviceHandler, Ipv6DeviceHandler, Ipv6LinkLayerAddr,
60};
61use crate::internal::local_delivery::{IpHeaderInfo, LocalDeliveryPacketInfo, ReceiveIpPacketMeta};
62use crate::internal::path_mtu::PmtuHandler;
63use crate::internal::socket::{
64    DelegatedRouteResolutionOptions, DelegatedSendOptions, IpSocketArgs, IpSocketHandler,
65    OptionDelegationMarker, RouteResolutionOptions, SendOptions,
66};
67
68/// The IP packet hop limit for all NDP packets.
69///
70/// See [RFC 4861 section 4.1], [RFC 4861 section 4.2], [RFC 4861 section 4.2],
71/// [RFC 4861 section 4.3], [RFC 4861 section 4.4], and [RFC 4861 section 4.5]
72/// for more information.
73///
74/// [RFC 4861 section 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
75/// [RFC 4861 section 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
76/// [RFC 4861 section 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
77/// [RFC 4861 section 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
78/// [RFC 4861 section 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
79pub const REQUIRED_NDP_IP_PACKET_HOP_LIMIT: u8 = 255;
80
81/// The default number of ICMP error messages to send per second.
82///
83/// Beyond this rate, error messages will be silently dropped.
84///
85/// The current value (1000) was inspired by Netstack2 (gVisor).
86// TODO(https://fxbug.dev/407541323): Consider tuning the ICMP rate limiting
87// behavior to conform more closely to Linux.
88pub const DEFAULT_ERRORS_PER_SECOND: u64 = 1000;
89/// The IP layer's ICMP state.
90#[derive(GenericOverIp)]
91#[generic_over_ip(I, Ip)]
92pub struct IcmpState<I: IpExt, BT: IcmpBindingsTypes> {
93    error_send_bucket: Mutex<IpMarked<I, TokenBucket<BT::Instant>>>,
94    /// ICMP transmit counters.
95    pub tx_counters: IcmpTxCounters<I>,
96    /// ICMP receive counters.
97    pub rx_counters: IcmpRxCounters<I>,
98}
99
100impl<I, BT> OrderedLockAccess<IpMarked<I, TokenBucket<BT::Instant>>> for IcmpState<I, BT>
101where
102    I: IpExt,
103    BT: IcmpBindingsTypes,
104{
105    type Lock = Mutex<IpMarked<I, TokenBucket<BT::Instant>>>;
106    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
107        OrderedLockRef::new(&self.error_send_bucket)
108    }
109}
110
111/// ICMP tx path counters.
112pub type IcmpTxCounters<I> = IpMarked<I, IcmpTxCountersInner>;
113
114/// ICMP tx path counters.
115#[derive(Default)]
116pub struct IcmpTxCountersInner {
117    /// Count of reply messages sent.
118    pub reply: Counter,
119    /// Count of protocol unreachable messages sent.
120    pub protocol_unreachable: Counter,
121    /// Count of host/address unreachable messages sent.
122    pub address_unreachable: Counter,
123    /// Count of port unreachable messages sent.
124    pub port_unreachable: Counter,
125    /// Count of net unreachable messages sent.
126    pub net_unreachable: Counter,
127    /// Count of ttl expired messages sent.
128    pub ttl_expired: Counter,
129    /// Count of packet too big messages sent.
130    pub packet_too_big: Counter,
131    /// Count of parameter problem messages sent.
132    pub parameter_problem: Counter,
133    /// Count of destination unreachable messages sent.
134    pub dest_unreachable: Counter,
135    /// Count of error messages sent.
136    pub error: Counter,
137}
138
139/// ICMP rx path counters.
140pub type IcmpRxCounters<I> = IpMarked<I, IcmpRxCountersInner>;
141
142/// ICMP rx path counters.
143#[derive(Default)]
144pub struct IcmpRxCountersInner {
145    /// Count of error messages received.
146    pub error: Counter,
147    /// Count of error messages delivered to the transport layer.
148    pub error_delivered_to_transport_layer: Counter,
149    /// Count of error messages delivered to a socket.
150    pub error_delivered_to_socket: Counter,
151    /// Count of echo request messages received.
152    pub echo_request: Counter,
153    /// Count of echo reply messages received.
154    pub echo_reply: Counter,
155    /// Count of timestamp request messages received.
156    pub timestamp_request: Counter,
157    /// Count of destination unreachable messages received.
158    pub dest_unreachable: Counter,
159    /// Count of time exceeded messages received.
160    pub time_exceeded: Counter,
161    /// Count of parameter problem messages received.
162    pub parameter_problem: Counter,
163    /// Count of packet too big messages received.
164    pub packet_too_big: Counter,
165    /// Count of ICMP Echo datagrams that could not be delivered to the socket
166    /// because its receive buffer was full.
167    pub queue_full: Counter,
168}
169
170/// Receive NDP counters.
171#[derive(Default)]
172pub struct NdpRxCounters {
173    /// Count of neighbor solicitation messages received.
174    pub neighbor_solicitation: Counter,
175    /// Count of neighbor advertisement messages received.
176    pub neighbor_advertisement: Counter,
177    /// Count of router advertisement messages received.
178    pub router_advertisement: Counter,
179    /// Count of router solicitation messages received.
180    pub router_solicitation: Counter,
181}
182
183/// Transmit NDP counters.
184#[derive(Default)]
185pub struct NdpTxCounters {
186    /// Count of neighbor advertisement messages sent.
187    pub neighbor_advertisement: Counter,
188    /// Count of neighbor solicitation messages sent.
189    pub neighbor_solicitation: Counter,
190}
191
192/// Counters for NDP messages.
193#[derive(Default)]
194pub struct NdpCounters {
195    /// Receive counters.
196    pub rx: NdpRxCounters,
197    /// Transmit counters.
198    pub tx: NdpTxCounters,
199}
200
201/// A builder for ICMPv4 state.
202#[derive(Copy, Clone)]
203pub struct Icmpv4StateBuilder {
204    send_timestamp_reply: bool,
205    errors_per_second: u64,
206}
207
208impl Default for Icmpv4StateBuilder {
209    fn default() -> Icmpv4StateBuilder {
210        Icmpv4StateBuilder {
211            send_timestamp_reply: false,
212            errors_per_second: DEFAULT_ERRORS_PER_SECOND,
213        }
214    }
215}
216
217impl Icmpv4StateBuilder {
218    /// Enable or disable replying to ICMPv4 Timestamp Request messages with
219    /// Timestamp Reply messages (default: disabled).
220    ///
221    /// Enabling this can introduce a very minor vulnerability in which an
222    /// attacker can learn the system clock's time, which in turn can aid in
223    /// attacks against time-based authentication systems.
224    pub fn send_timestamp_reply(&mut self, send_timestamp_reply: bool) -> &mut Self {
225        self.send_timestamp_reply = send_timestamp_reply;
226        self
227    }
228
229    /// Builds an [`Icmpv4State`].
230    pub fn build<BT: IcmpBindingsTypes>(self) -> Icmpv4State<BT> {
231        Icmpv4State {
232            inner: IcmpState {
233                error_send_bucket: Mutex::new(IpMarked::new(TokenBucket::new(
234                    self.errors_per_second,
235                ))),
236                tx_counters: Default::default(),
237                rx_counters: Default::default(),
238            },
239            send_timestamp_reply: self.send_timestamp_reply,
240        }
241    }
242}
243
244/// The state associated with the ICMPv4 protocol.
245pub struct Icmpv4State<BT: IcmpBindingsTypes> {
246    /// The inner common ICMP state.
247    pub inner: IcmpState<Ipv4, BT>,
248    /// Whether the stack is configured to send ICMP timestamp replies.
249    pub send_timestamp_reply: bool,
250}
251
252impl<BT: IcmpBindingsTypes> AsRef<IcmpState<Ipv4, BT>> for Icmpv4State<BT> {
253    fn as_ref(&self) -> &IcmpState<Ipv4, BT> {
254        &self.inner
255    }
256}
257
258impl<BT: IcmpBindingsTypes> AsMut<IcmpState<Ipv4, BT>> for Icmpv4State<BT> {
259    fn as_mut(&mut self) -> &mut IcmpState<Ipv4, BT> {
260        &mut self.inner
261    }
262}
263
264/// A builder for ICMPv6 state.
265#[derive(Copy, Clone)]
266pub(crate) struct Icmpv6StateBuilder {
267    errors_per_second: u64,
268}
269
270impl Default for Icmpv6StateBuilder {
271    fn default() -> Icmpv6StateBuilder {
272        Icmpv6StateBuilder { errors_per_second: DEFAULT_ERRORS_PER_SECOND }
273    }
274}
275
276impl Icmpv6StateBuilder {
277    pub(crate) fn build<BT: IcmpBindingsTypes>(self) -> Icmpv6State<BT> {
278        Icmpv6State {
279            inner: IcmpState {
280                error_send_bucket: Mutex::new(IpMarked::new(TokenBucket::new(
281                    self.errors_per_second,
282                ))),
283                tx_counters: Default::default(),
284                rx_counters: Default::default(),
285            },
286            ndp_counters: Default::default(),
287        }
288    }
289}
290
291/// The state associated with the ICMPv6 protocol.
292pub struct Icmpv6State<BT: IcmpBindingsTypes> {
293    /// The inner common ICMP state.
294    pub inner: IcmpState<Ipv6, BT>,
295    /// Neighbor discovery protocol counters.
296    pub ndp_counters: NdpCounters,
297}
298
299impl<BT: IcmpBindingsTypes> AsRef<IcmpState<Ipv6, BT>> for Icmpv6State<BT> {
300    fn as_ref(&self) -> &IcmpState<Ipv6, BT> {
301        &self.inner
302    }
303}
304
305impl<BT: IcmpBindingsTypes> AsMut<IcmpState<Ipv6, BT>> for Icmpv6State<BT> {
306    fn as_mut(&mut self) -> &mut IcmpState<Ipv6, BT> {
307        &mut self.inner
308    }
309}
310
311/// An extension trait providing ICMP handler properties.
312pub trait IcmpHandlerIpExt: IpExt {
313    type SourceAddress: Witness<Self::Addr>;
314    type IcmpError;
315
316    /// A try-conversion from [`Self::RecvSrcAddr`] to [`Self::SourceAddress`].
317    fn received_source_as_icmp_source(src: Self::RecvSrcAddr) -> Option<Self::SourceAddress>;
318
319    /// An IP-specific constructor for TtlExpired ICMP errors.
320    fn new_ttl_expired<B: SplitByteSlice>(
321        proto: Self::Proto,
322        header_len: usize,
323        meta: <Self::Packet<B> as IpPacket<B, Self>>::VersionSpecificMeta,
324    ) -> Self::IcmpError;
325
326    /// An IP-specific optional-constructor for MTU Exceeded ICMP errors.
327    fn new_mtu_exceeded(proto: Self::Proto, header_len: usize, mtu: Mtu)
328    -> Option<Self::IcmpError>;
329}
330
331impl IcmpHandlerIpExt for Ipv4 {
332    type SourceAddress = NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>;
333    type IcmpError = Icmpv4Error;
334    fn received_source_as_icmp_source(
335        src: Ipv4SourceAddr,
336    ) -> Option<NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>> {
337        match src {
338            Ipv4SourceAddr::Specified(src) => Some(src),
339            Ipv4SourceAddr::Unspecified => None,
340        }
341    }
342    fn new_ttl_expired<B: SplitByteSlice>(
343        proto: Ipv4Proto,
344        header_len: usize,
345        Ipv4OnlyMeta { id: _, fragment_type }: Ipv4OnlyMeta,
346    ) -> Icmpv4Error {
347        Icmpv4Error { kind: Icmpv4ErrorKind::TtlExpired { proto, fragment_type }, header_len }
348    }
349    fn new_mtu_exceeded(_proto: Ipv4Proto, _header_len: usize, _mtu: Mtu) -> Option<Icmpv4Error> {
350        // NB: ICMPv4 has no representation of MTU exceeded errors.
351        None
352    }
353}
354
355impl IcmpHandlerIpExt for Ipv6 {
356    type SourceAddress = UnicastAddr<Ipv6Addr>;
357    type IcmpError = Icmpv6ErrorKind;
358    fn received_source_as_icmp_source(src: Ipv6SourceAddr) -> Option<UnicastAddr<Ipv6Addr>> {
359        match src {
360            Ipv6SourceAddr::Unicast(src) => Some(src.get()),
361            Ipv6SourceAddr::Unspecified => None,
362        }
363    }
364    fn new_ttl_expired<B: SplitByteSlice>(
365        proto: Ipv6Proto,
366        header_len: usize,
367        _meta: (),
368    ) -> Icmpv6ErrorKind {
369        Icmpv6ErrorKind::TtlExpired { proto, header_len }
370    }
371    fn new_mtu_exceeded(proto: Ipv6Proto, header_len: usize, mtu: Mtu) -> Option<Icmpv6ErrorKind> {
372        Some(Icmpv6ErrorKind::PacketTooBig { proto, header_len, mtu })
373    }
374}
375
376/// A kind of ICMPv4 error.
377pub(crate) enum Icmpv4ErrorKind {
378    ParameterProblem {
379        code: Icmpv4ParameterProblemCode,
380        pointer: u8,
381        fragment_type: Ipv4FragmentType,
382    },
383    TtlExpired {
384        proto: Ipv4Proto,
385        fragment_type: Ipv4FragmentType,
386    },
387    NetUnreachable {
388        proto: Ipv4Proto,
389        fragment_type: Ipv4FragmentType,
390    },
391    ProtocolUnreachable,
392    PortUnreachable,
393}
394
395/// An ICMPv4 error.
396pub struct Icmpv4Error {
397    pub(super) kind: Icmpv4ErrorKind,
398    pub(super) header_len: usize,
399}
400
401/// A kind of ICMPv6 error.
402pub enum Icmpv6ErrorKind {
403    ParameterProblem { code: Icmpv6ParameterProblemCode, pointer: u32, allow_dst_multicast: bool },
404    TtlExpired { proto: Ipv6Proto, header_len: usize },
405    NetUnreachable { proto: Ipv6Proto, header_len: usize },
406    PacketTooBig { proto: Ipv6Proto, header_len: usize, mtu: Mtu },
407    ProtocolUnreachable { header_len: usize },
408    PortUnreachable,
409}
410
411/// The handler exposed by ICMP.
412pub trait IcmpErrorHandler<I: IcmpHandlerIpExt, BC>: DeviceIdContext<AnyDevice> {
413    /// Sends an error message in response to an incoming packet.
414    ///
415    /// `src_ip` and `dst_ip` are the source and destination addresses of the
416    /// incoming packet.
417    fn send_icmp_error_message<B: BufferMut>(
418        &mut self,
419        bindings_ctx: &mut BC,
420        device: &Self::DeviceId,
421        frame_dst: Option<FrameDestination>,
422        src_ip: I::SourceAddress,
423        dst_ip: SpecifiedAddr<I::Addr>,
424        original_packet: B,
425        error: I::IcmpError,
426        marks: &Marks,
427    );
428}
429
430impl<BC: IcmpBindingsContext, CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>>
431    IcmpErrorHandler<Ipv4, BC> for CC
432{
433    fn send_icmp_error_message<B: BufferMut>(
434        &mut self,
435        bindings_ctx: &mut BC,
436        device: &CC::DeviceId,
437        frame_dst: Option<FrameDestination>,
438        src_ip: NonMulticastAddr<SpecifiedAddr<Ipv4Addr>>,
439        dst_ip: SpecifiedAddr<Ipv4Addr>,
440        original_packet: B,
441        Icmpv4Error { kind, header_len }: Icmpv4Error,
442        marks: &Marks,
443    ) {
444        let src_ip = SocketIpAddr::new_ipv4_specified(src_ip.get());
445        let dst_ip = SocketIpAddr::new_ipv4_specified(dst_ip);
446        match kind {
447            Icmpv4ErrorKind::ParameterProblem { code, pointer, fragment_type } => {
448                send_icmpv4_parameter_problem(
449                    self,
450                    bindings_ctx,
451                    device,
452                    frame_dst,
453                    src_ip,
454                    dst_ip,
455                    code,
456                    Icmpv4ParameterProblem::new(pointer),
457                    original_packet,
458                    header_len,
459                    fragment_type,
460                    marks,
461                )
462            }
463            Icmpv4ErrorKind::TtlExpired { proto, fragment_type } => send_icmpv4_ttl_expired(
464                self,
465                bindings_ctx,
466                device,
467                frame_dst,
468                src_ip,
469                dst_ip,
470                proto,
471                original_packet,
472                header_len,
473                fragment_type,
474                marks,
475            ),
476            Icmpv4ErrorKind::NetUnreachable { proto, fragment_type } => {
477                send_icmpv4_net_unreachable(
478                    self,
479                    bindings_ctx,
480                    device,
481                    frame_dst,
482                    src_ip,
483                    dst_ip,
484                    proto,
485                    original_packet,
486                    header_len,
487                    fragment_type,
488                    marks,
489                )
490            }
491            Icmpv4ErrorKind::ProtocolUnreachable => send_icmpv4_protocol_unreachable(
492                self,
493                bindings_ctx,
494                device,
495                frame_dst,
496                src_ip,
497                dst_ip,
498                original_packet,
499                header_len,
500                marks,
501            ),
502            Icmpv4ErrorKind::PortUnreachable => send_icmpv4_port_unreachable(
503                self,
504                bindings_ctx,
505                device,
506                frame_dst,
507                src_ip,
508                dst_ip,
509                original_packet,
510                header_len,
511                marks,
512            ),
513        }
514    }
515}
516
517impl<BC: IcmpBindingsContext, CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>>
518    IcmpErrorHandler<Ipv6, BC> for CC
519{
520    fn send_icmp_error_message<B: BufferMut>(
521        &mut self,
522        bindings_ctx: &mut BC,
523        device: &CC::DeviceId,
524        frame_dst: Option<FrameDestination>,
525        src_ip: UnicastAddr<Ipv6Addr>,
526        dst_ip: SpecifiedAddr<Ipv6Addr>,
527        original_packet: B,
528        error: Icmpv6ErrorKind,
529        marks: &Marks,
530    ) {
531        let src_ip: SocketIpAddr<Ipv6Addr> = match src_ip.into_specified().try_into() {
532            Ok(addr) => addr,
533            Err(AddrIsMappedError {}) => {
534                trace!("send_icmpv6_error_message: src_ip is mapped");
535                return;
536            }
537        };
538        let dst_ip: SocketIpAddr<Ipv6Addr> = match dst_ip.try_into() {
539            Ok(addr) => addr,
540            Err(AddrIsMappedError {}) => {
541                trace!("send_icmpv6_error_message: dst_ip is mapped");
542                return;
543            }
544        };
545
546        match error {
547            Icmpv6ErrorKind::ParameterProblem { code, pointer, allow_dst_multicast } => {
548                send_icmpv6_parameter_problem(
549                    self,
550                    bindings_ctx,
551                    device,
552                    frame_dst,
553                    src_ip,
554                    dst_ip,
555                    code,
556                    Icmpv6ParameterProblem::new(pointer),
557                    original_packet,
558                    allow_dst_multicast,
559                    marks,
560                )
561            }
562            Icmpv6ErrorKind::TtlExpired { proto, header_len } => send_icmpv6_ttl_expired(
563                self,
564                bindings_ctx,
565                device,
566                frame_dst,
567                src_ip,
568                dst_ip,
569                proto,
570                original_packet,
571                header_len,
572                marks,
573            ),
574            Icmpv6ErrorKind::NetUnreachable { proto, header_len } => send_icmpv6_net_unreachable(
575                self,
576                bindings_ctx,
577                device,
578                frame_dst,
579                src_ip,
580                dst_ip,
581                proto,
582                original_packet,
583                header_len,
584                marks,
585            ),
586            Icmpv6ErrorKind::PacketTooBig { proto, header_len, mtu } => send_icmpv6_packet_too_big(
587                self,
588                bindings_ctx,
589                device,
590                frame_dst,
591                src_ip,
592                dst_ip,
593                proto,
594                mtu,
595                original_packet,
596                header_len,
597                marks,
598            ),
599            Icmpv6ErrorKind::ProtocolUnreachable { header_len } => {
600                send_icmpv6_protocol_unreachable(
601                    self,
602                    bindings_ctx,
603                    device,
604                    frame_dst,
605                    src_ip,
606                    dst_ip,
607                    original_packet,
608                    header_len,
609                    marks,
610                )
611            }
612            Icmpv6ErrorKind::PortUnreachable => send_icmpv6_port_unreachable(
613                self,
614                bindings_ctx,
615                device,
616                frame_dst,
617                src_ip,
618                dst_ip,
619                original_packet,
620                marks,
621            ),
622        }
623    }
624}
625
626/// A marker for all the contexts provided by bindings require by the ICMP
627/// module.
628pub trait IcmpBindingsContext: InstantContext + RngContext + IcmpBindingsTypes {}
629impl<BC> IcmpBindingsContext for BC where
630    BC: InstantContext + RngContext + IcmpBindingsTypes + IcmpBindingsTypes
631{
632}
633
634/// A marker trait for all bindings types required by the ICMP module.
635pub trait IcmpBindingsTypes: InstantBindingsTypes + TxMetadataBindingsTypes {}
636impl<BT: InstantBindingsTypes + TxMetadataBindingsTypes> IcmpBindingsTypes for BT {}
637
638/// Empty trait to work around coherence issues.
639///
640/// This serves only to convince the coherence checker that a particular blanket
641/// trait implementation could only possibly conflict with other blanket impls
642/// in this crate. It can be safely implemented for any type.
643/// TODO(https://github.com/rust-lang/rust/issues/97811): Remove this once the
644/// coherence checker doesn't require it.
645pub trait IcmpStateContext {}
646
647/// A marker trait to prevent integration from creating a recursive loop when
648/// handling Echo sockets.
649///
650/// This is a requirement for [`InnerIcmpContext::EchoTransportContext`] which
651/// disallows the integration layer from using [`IcmpIpTransportContext`] as the
652/// associated type, which would create a recursive loop.
653///
654/// By *not implementing* this trait for [`IcmpIpTransporContext`] we prevent
655/// the mistake.
656pub trait EchoTransportContextMarker {}
657
658/// The execution context shared by ICMP(v4) and ICMPv6 for the internal
659/// operations of the IP stack.
660pub trait InnerIcmpContext<I: IpExt + FilterIpExt, BC: IcmpBindingsTypes>:
661    IpSocketHandler<I, BC>
662{
663    /// A type implementing [`IpTransportContext`] that handles ICMP Echo
664    /// replies.
665    type EchoTransportContext: IpTransportContext<I, BC, Self> + EchoTransportContextMarker;
666
667    // TODO(joshlf): If we end up needing to respond to these messages with new
668    // outbound packets, then perhaps it'd be worth passing the original buffer
669    // so that it can be reused?
670    //
671    // NOTE(joshlf): We don't guarantee the packet body length here for two
672    // reasons:
673    // - It's possible that some IPv4 protocol does or will exist for which
674    //   valid packets are less than 8 bytes in length. If we were to reject all
675    //   packets with bodies of less than 8 bytes, we might silently discard
676    //   legitimate error messages for such protocols.
677    // - Even if we were to guarantee this, there's no good way to encode such a
678    //   guarantee in the type system, and so the caller would have no recourse
679    //   but to panic, and panics have a habit of becoming bugs or DoS
680    //   vulnerabilities when invariants change.
681
682    /// Receives an ICMP error message and demultiplexes it to a transport layer
683    /// protocol.
684    ///
685    /// All arguments beginning with `original_` are fields from the IP packet
686    /// that triggered the error. The `original_body` is provided here so that
687    /// the error can be associated with a transport-layer socket. `device`
688    /// identifies the device on which the packet was received.
689    ///
690    /// While ICMPv4 error messages are supposed to contain the first 8 bytes of
691    /// the body of the offending packet, and ICMPv6 error messages are supposed
692    /// to contain as much of the offending packet as possible without violating
693    /// the IPv6 minimum MTU, the caller does NOT guarantee that either of these
694    /// hold. It is `receive_icmp_error`'s responsibility to handle any length
695    /// of `original_body`, and to perform any necessary validation.
696    fn receive_icmp_error(
697        &mut self,
698        bindings_ctx: &mut BC,
699        device: &Self::DeviceId,
700        original_src_ip: Option<SpecifiedAddr<I::Addr>>,
701        original_dst_ip: SpecifiedAddr<I::Addr>,
702        original_proto: I::Proto,
703        original_body: &[u8],
704        err: I::ErrorCode,
705    );
706
707    /// Calls the function with a mutable reference to ICMP error send tocket
708    /// bucket.
709    fn with_error_send_bucket_mut<O, F: FnOnce(&mut TokenBucket<BC::Instant>) -> O>(
710        &mut self,
711        cb: F,
712    ) -> O;
713}
714
715/// The execution context for ICMPv4.
716///
717/// `InnerIcmpv4Context` is a shorthand for a larger collection of traits.
718pub trait InnerIcmpv4Context<BC: IcmpBindingsTypes>: InnerIcmpContext<Ipv4, BC> {
719    /// Returns true if a timestamp reply may be sent.
720    fn should_send_timestamp_reply(&self) -> bool;
721}
722
723/// The execution context for ICMPv6.
724///
725/// `InnerIcmpv6Context` is a shorthand for a larger collection of traits.
726pub trait InnerIcmpv6Context<BC: IcmpBindingsTypes>: InnerIcmpContext<Ipv6, BC> {}
727impl<BC: IcmpBindingsTypes, CC: InnerIcmpContext<Ipv6, BC>> InnerIcmpv6Context<BC> for CC {}
728
729/// Attempt to send an ICMP or ICMPv6 error message, applying a rate limit.
730///
731/// `try_send_error!($core_ctx, $bindings_ctx, $e)` attempts to consume a token from the
732/// token bucket at `$core_ctx.get_state_mut().error_send_bucket`. If it
733/// succeeds, it invokes the expression `$e`, and otherwise does nothing. It
734/// assumes that the type of `$e` is `Result<(), _>` and, in the case that the
735/// rate limit is exceeded and it does not invoke `$e`, returns `Ok(())`.
736///
737/// [RFC 4443 Section 2.4] (f) requires that we MUST limit the rate of outbound
738/// ICMPv6 error messages. To our knowledge, there is no similar requirement for
739/// ICMPv4, but the same rationale applies, so we do it for ICMPv4 as well.
740///
741/// [RFC 4443 Section 2.4]: https://tools.ietf.org/html/rfc4443#section-2.4
742macro_rules! try_send_error {
743    ($core_ctx:expr, $bindings_ctx:expr, $e:expr) => {{
744        let send = $core_ctx.with_error_send_bucket_mut(|error_send_bucket| {
745            error_send_bucket.try_take($bindings_ctx)
746        });
747
748        if send {
749            $core_ctx.counters().error.increment();
750            $e
751        } else {
752            trace!("ip::icmp::try_send_error!: dropping rate-limited ICMP error message");
753            Ok(())
754        }
755    }};
756}
757
758/// An implementation of [`IpTransportContext`] for ICMP.
759pub enum IcmpIpTransportContext {}
760
761fn receive_ip_transport_icmp_error<
762    I: IpExt + FilterIpExt,
763    CC: InnerIcmpContext<I, BC> + CounterContext<IcmpRxCounters<I>>,
764    BC: IcmpBindingsContext,
765>(
766    core_ctx: &mut CC,
767    bindings_ctx: &mut BC,
768    device: &CC::DeviceId,
769    original_src_ip: Option<SpecifiedAddr<I::Addr>>,
770    original_dst_ip: SpecifiedAddr<I::Addr>,
771    original_body: &[u8],
772    err: I::ErrorCode,
773) {
774    core_ctx.counters().error_delivered_to_transport_layer.increment();
775    trace!("IcmpIpTransportContext::receive_icmp_error({:?})", err);
776
777    let mut parse_body = original_body;
778    match parse_body.parse::<IcmpPacketRaw<I, _, IcmpEchoRequest>>() {
779        // Only pass things along to the Echo socket layer if this is an echo
780        // request.
781        Ok(_echo_request) => (),
782        Err(_) => {
783            // NOTE: This might just mean that the error message was in response
784            // to a packet that we sent that wasn't an echo request, so we just
785            // silently ignore it.
786            return;
787        }
788    }
789
790    <CC::EchoTransportContext as IpTransportContext<I, BC, CC>>::receive_icmp_error(
791        core_ctx,
792        bindings_ctx,
793        device,
794        original_src_ip,
795        original_dst_ip,
796        original_body,
797        err,
798    );
799}
800
801impl<
802    BC: IcmpBindingsContext,
803    CC: InnerIcmpv4Context<BC>
804        + PmtuHandler<Ipv4, BC>
805        + CounterContext<IcmpRxCounters<Ipv4>>
806        + CounterContext<IcmpTxCounters<Ipv4>>,
807> IpTransportContext<Ipv4, BC, CC> for IcmpIpTransportContext
808{
809    fn receive_icmp_error(
810        core_ctx: &mut CC,
811        bindings_ctx: &mut BC,
812        device: &CC::DeviceId,
813        original_src_ip: Option<SpecifiedAddr<Ipv4Addr>>,
814        original_dst_ip: SpecifiedAddr<Ipv4Addr>,
815        original_body: &[u8],
816        err: Icmpv4ErrorCode,
817    ) {
818        receive_ip_transport_icmp_error(
819            core_ctx,
820            bindings_ctx,
821            device,
822            original_src_ip,
823            original_dst_ip,
824            original_body,
825            err,
826        )
827    }
828
829    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<Ipv4>>(
830        core_ctx: &mut CC,
831        bindings_ctx: &mut BC,
832        device: &CC::DeviceId,
833        src_ip: Ipv4SourceAddr,
834        dst_ip: SpecifiedAddr<Ipv4Addr>,
835        mut buffer: B,
836        info: &LocalDeliveryPacketInfo<Ipv4, H>,
837    ) -> Result<(), (B, TransportReceiveError)> {
838        let LocalDeliveryPacketInfo { meta, header_info: _, marks } = info;
839        let ReceiveIpPacketMeta { broadcast: _, transparent_override } = meta;
840        if let Some(delivery) = transparent_override {
841            unreachable!(
842                "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
843                transparent proxy rules can only be configured for TCP and UDP packets"
844            );
845        }
846
847        trace!(
848            "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet({}, {})",
849            src_ip, dst_ip
850        );
851        let packet =
852            match buffer.parse_with::<_, Icmpv4Packet<_>>(IcmpParseArgs::new(src_ip, dst_ip)) {
853                Ok(packet) => packet,
854                Err(_) => return Ok(()), // TODO(joshlf): Do something else here?
855            };
856
857        match packet {
858            Icmpv4Packet::EchoRequest(echo_request) => {
859                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx).echo_request.increment();
860
861                if let Ipv4SourceAddr::Specified(src_ip) = src_ip {
862                    let req = *echo_request.message();
863                    let code = echo_request.code();
864                    let (local_ip, remote_ip) = (dst_ip, src_ip);
865                    debug!(
866                        "replying to ICMP echo request from {remote_ip} to {local_ip}%{device:?}: \
867                        id={}, seq={}",
868                        req.id(),
869                        req.seq()
870                    );
871                    send_icmp_reply(
872                        core_ctx,
873                        bindings_ctx,
874                        device,
875                        SocketIpAddr::new_ipv4_specified(remote_ip.get()),
876                        SocketIpAddr::new_ipv4_specified(local_ip),
877                        |src_ip| {
878                            IcmpPacketBuilder::<Ipv4, _>::new(src_ip, *remote_ip, code, req.reply())
879                                .wrap_body(buffer)
880                        },
881                        &WithMarks(marks),
882                    );
883                } else {
884                    trace!(
885                        "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
886                        Received echo request with an unspecified source address"
887                    );
888                }
889            }
890            Icmpv4Packet::EchoReply(echo_reply) => {
891                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx).echo_reply.increment();
892                trace!(
893                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
894                    Received an EchoReply message"
895                );
896                let parse_metadata = echo_reply.parse_metadata();
897                buffer.undo_parse(parse_metadata);
898                return <CC::EchoTransportContext
899                            as IpTransportContext<Ipv4, BC, CC>>::receive_ip_packet(
900                        core_ctx,
901                        bindings_ctx,
902                        device,
903                        src_ip,
904                        dst_ip,
905                        buffer,
906                        info,
907                );
908            }
909            Icmpv4Packet::TimestampRequest(timestamp_request) => {
910                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
911                    .timestamp_request
912                    .increment();
913                if let Ipv4SourceAddr::Specified(src_ip) = src_ip {
914                    if core_ctx.should_send_timestamp_reply() {
915                        trace!(
916                            "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
917                            receive_ip_packet: Responding to Timestamp Request message"
918                        );
919                        // We're supposed to respond with the time that we
920                        // processed this message as measured in milliseconds
921                        // since midnight UT. However, that would require that
922                        // we knew the local time zone and had a way to convert
923                        // `InstantContext::Instant` to a `u32` value. We can't
924                        // do that, and probably don't want to introduce all of
925                        // the machinery necessary just to support this one use
926                        // case. Luckily, RFC 792 page 17 provides us with an
927                        // out:
928                        //
929                        //   If the time is not available in miliseconds [sic]
930                        //   or cannot be provided with respect to midnight UT
931                        //   then any time can be inserted in a timestamp
932                        //   provided the high order bit of the timestamp is
933                        //   also set to indicate this non-standard value.
934                        //
935                        // Thus, we provide a zero timestamp with the high order
936                        // bit set.
937                        const NOW: u32 = 0x80000000;
938                        let reply = timestamp_request.message().reply(NOW, NOW);
939                        let (local_ip, remote_ip) = (dst_ip, src_ip);
940                        // We don't actually want to use any of the _contents_
941                        // of the buffer, but we would like to reuse it as
942                        // scratch space. Eventually, `IcmpPacketBuilder` will
943                        // implement `InnerPacketBuilder` for messages without
944                        // bodies, but until that happens, we need to give it an
945                        // empty buffer.
946                        buffer.shrink_front_to(0);
947                        send_icmp_reply(
948                            core_ctx,
949                            bindings_ctx,
950                            device,
951                            SocketIpAddr::new_ipv4_specified(remote_ip.get()),
952                            SocketIpAddr::new_ipv4_specified(local_ip),
953                            |src_ip| {
954                                IcmpPacketBuilder::<Ipv4, _>::new(
955                                    src_ip,
956                                    *remote_ip,
957                                    IcmpZeroCode,
958                                    reply,
959                                )
960                                .wrap_body(buffer)
961                            },
962                            &WithMarks(marks),
963                        );
964                    } else {
965                        trace!(
966                            "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
967                            receive_ip_packet: Silently ignoring Timestamp Request message"
968                        );
969                    }
970                } else {
971                    trace!(
972                        "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
973                        receive_ip_packet: Received timestamp request with an unspecified source \
974                        address"
975                    );
976                }
977            }
978            Icmpv4Packet::TimestampReply(_) => {
979                // TODO(joshlf): Support sending Timestamp Requests and
980                // receiving Timestamp Replies?
981                debug!(
982                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
983                    Received unsolicited Timestamp Reply message"
984                );
985            }
986            Icmpv4Packet::DestUnreachable(dest_unreachable) => {
987                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
988                    .dest_unreachable
989                    .increment();
990                trace!(
991                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
992                    Received a Destination Unreachable message"
993                );
994
995                let error = if dest_unreachable.code()
996                    == Icmpv4DestUnreachableCode::FragmentationRequired
997                {
998                    let mtu = if let Some(next_hop_mtu) = dest_unreachable.message().next_hop_mtu()
999                    {
1000                        // We are updating the path MTU from the destination
1001                        // address of this `packet` (which is an IP address on
1002                        // this node) to some remote (identified by the source
1003                        // address of this `packet`).
1004                        //
1005                        // `update_pmtu_if_less` will only update the PMTU if
1006                        // the Dest Unreachable message's MTU field had a value
1007                        // that was at least the IPv4 minimum MTU (which is
1008                        // required by IPv4 RFC 791).
1009                        core_ctx.update_pmtu_if_less(
1010                            bindings_ctx,
1011                            dst_ip.get(),
1012                            src_ip.get(),
1013                            Mtu::new(u32::from(next_hop_mtu.get())),
1014                        )
1015                    } else {
1016                        // If the Next-Hop MTU from an incoming ICMP message is
1017                        // `0`, then we assume the source node of the ICMP
1018                        // message does not implement RFC 1191 and therefore
1019                        // does not actually use the Next-Hop MTU field and
1020                        // still considers it as an unused field.
1021                        //
1022                        // In this case, the only information we have is the
1023                        // size of the original IP packet that was too big (the
1024                        // original packet header should be included in the ICMP
1025                        // response). Here we will simply reduce our PMTU
1026                        // estimate to a value less than the total length of the
1027                        // original packet. See RFC 1191 Section 5.
1028                        //
1029                        // `update_pmtu_next_lower` may return an error, but it
1030                        // will only happen if no valid lower value exists from
1031                        // the original packet's length. It is safe to silently
1032                        // ignore the error when we have no valid lower PMTU
1033                        // value as the node from `src_ip` would not be IP RFC
1034                        // compliant and we expect this to be very rare (for
1035                        // IPv4, the lowest MTU value for a link can be 68
1036                        // bytes).
1037                        let (original_packet_buf, inner_body) = dest_unreachable.body().bytes();
1038                        // Note: ICMP Dest Unreachable messages don't have a variable size body.
1039                        debug_assert!(inner_body.is_none());
1040                        if original_packet_buf.len() >= 4 {
1041                            // We need the first 4 bytes as the total length
1042                            // field is at bytes 2/3 of the original packet
1043                            // buffer.
1044                            let total_len =
1045                                u16::from_be_bytes(original_packet_buf[2..4].try_into().unwrap());
1046
1047                            trace!(
1048                                "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
1049                                receive_ip_packet: Next-Hop MTU is 0 so using the next best PMTU \
1050                                value from {total_len}"
1051                            );
1052
1053                            core_ctx.update_pmtu_next_lower(
1054                                bindings_ctx,
1055                                dst_ip.get(),
1056                                src_ip.get(),
1057                                Mtu::new(u32::from(total_len)),
1058                            )
1059                        } else {
1060                            // Ok to silently ignore as RFC 792 requires nodes
1061                            // to send the original IP packet header + 64 bytes
1062                            // of the original IP packet's body so the node
1063                            // itself is already violating the RFC.
1064                            trace!(
1065                                "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
1066                                receive_ip_packet: Original packet buf is too small to get \
1067                                original packet len so ignoring"
1068                            );
1069                            None
1070                        }
1071                    };
1072                    mtu.and_then(|mtu| {
1073                        let mtu = u16::try_from(mtu.get()).unwrap_or(u16::MAX);
1074                        let mtu = NonZeroU16::new(mtu)?;
1075                        Some(Icmpv4ErrorCode::DestUnreachable(
1076                            dest_unreachable.code(),
1077                            IcmpDestUnreachable::new_for_frag_req(mtu),
1078                        ))
1079                    })
1080                } else {
1081                    Some(Icmpv4ErrorCode::DestUnreachable(
1082                        dest_unreachable.code(),
1083                        *dest_unreachable.message(),
1084                    ))
1085                };
1086
1087                if let Some(error) = error {
1088                    receive_icmpv4_error(core_ctx, bindings_ctx, device, &dest_unreachable, error);
1089                }
1090            }
1091            Icmpv4Packet::TimeExceeded(time_exceeded) => {
1092                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
1093                    .time_exceeded
1094                    .increment();
1095                trace!(
1096                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
1097                    Received a Time Exceeded message"
1098                );
1099
1100                receive_icmpv4_error(
1101                    core_ctx,
1102                    bindings_ctx,
1103                    device,
1104                    &time_exceeded,
1105                    Icmpv4ErrorCode::TimeExceeded(time_exceeded.code()),
1106                );
1107            }
1108            // TODO(https://fxbug.dev/323400954): Support ICMP Redirect.
1109            Icmpv4Packet::Redirect(_) => {
1110                debug!(
1111                    "Unimplemented: <IcmpIpTransportContext as IpTransportContext<Ipv4>>::\
1112                    receive_ip_packet::redirect"
1113                )
1114            }
1115            Icmpv4Packet::ParameterProblem(parameter_problem) => {
1116                CounterContext::<IcmpRxCounters<Ipv4>>::counters(core_ctx)
1117                    .parameter_problem
1118                    .increment();
1119                trace!(
1120                    "<IcmpIpTransportContext as IpTransportContext<Ipv4>>::receive_ip_packet: \
1121                    Received a Parameter Problem message"
1122                );
1123
1124                receive_icmpv4_error(
1125                    core_ctx,
1126                    bindings_ctx,
1127                    device,
1128                    &parameter_problem,
1129                    Icmpv4ErrorCode::ParameterProblem(parameter_problem.code()),
1130                );
1131            }
1132        }
1133
1134        Ok(())
1135    }
1136}
1137
1138/// A type to allow implementing the required filtering traits on a concrete
1139/// subset of message types.
1140#[allow(missing_docs)]
1141pub enum NdpMessage {
1142    NeighborSolicitation {
1143        message: NeighborSolicitation,
1144        code: <NeighborSolicitation as IcmpMessage<Ipv6>>::Code,
1145    },
1146
1147    RouterSolicitation {
1148        message: RouterSolicitation,
1149        code: <RouterSolicitation as IcmpMessage<Ipv6>>::Code,
1150    },
1151
1152    NeighborAdvertisement {
1153        message: NeighborAdvertisement,
1154        code: <NeighborAdvertisement as IcmpMessage<Ipv6>>::Code,
1155    },
1156}
1157
1158/// Sends an NDP packet from `device_id` with the provided parameters.
1159pub fn send_ndp_packet<BC, CC, S>(
1160    core_ctx: &mut CC,
1161    bindings_ctx: &mut BC,
1162    device_id: &CC::DeviceId,
1163    src_ip: Option<SpecifiedAddr<Ipv6Addr>>,
1164    dst_ip: SpecifiedAddr<Ipv6Addr>,
1165    body: S,
1166    message: NdpMessage,
1167) -> Result<(), IpSendFrameError<S>>
1168where
1169    CC: IpLayerHandler<Ipv6, BC>,
1170    S: Serializer + PartialSerializer,
1171    S::Buffer: BufferMut,
1172{
1173    macro_rules! send {
1174        ($message:expr, $code:expr) => {{
1175            // TODO(https://fxbug.dev/42177356): Send through ICMPv6 send path.
1176            let mut ser = IcmpPacketBuilder::<Ipv6, _>::new(
1177                src_ip.map_or(Ipv6::UNSPECIFIED_ADDRESS, |a| a.get()),
1178                dst_ip.get(),
1179                $code,
1180                $message,
1181            )
1182            .wrap_body(body);
1183            match IpLayerHandler::<Ipv6, _>::send_ip_packet_from_device(
1184                core_ctx,
1185                bindings_ctx,
1186                SendIpPacketMeta {
1187                    device: device_id,
1188                    src_ip,
1189                    dst_ip,
1190                    destination: IpPacketDestination::from_addr(dst_ip),
1191                    ttl: NonZeroU8::new(REQUIRED_NDP_IP_PACKET_HOP_LIMIT),
1192                    proto: Ipv6Proto::Icmpv6,
1193                    mtu: Mtu::no_limit(),
1194                    dscp_and_ecn: DscpAndEcn::default(),
1195                },
1196                DynTransportSerializer::new(&mut ser),
1197            ) {
1198                Ok(()) => Ok(()),
1199                Err(e) => Err(e
1200                    .map_serializer(|s| {
1201                        // Get rid of the borrow to the serializer.
1202                        let _: DynTransportSerializer<'_, _> = s;
1203                    })
1204                    .map_serializer(|()| ser.into_inner())),
1205            }
1206        }};
1207    }
1208
1209    match message {
1210        NdpMessage::NeighborSolicitation { message, code } => send!(message, code),
1211        NdpMessage::RouterSolicitation { message, code } => send!(message, code),
1212        NdpMessage::NeighborAdvertisement { message, code } => send!(message, code),
1213    }
1214}
1215
1216fn send_neighbor_advertisement<
1217    BC,
1218    CC: Ipv6DeviceHandler<BC>
1219        + IpDeviceHandler<Ipv6, BC>
1220        + IpLayerHandler<Ipv6, BC>
1221        + CounterContext<NdpCounters>,
1222>(
1223    core_ctx: &mut CC,
1224    bindings_ctx: &mut BC,
1225    device_id: &CC::DeviceId,
1226    solicited: bool,
1227    device_addr: UnicastAddr<Ipv6Addr>,
1228    dst_ip: SpecifiedAddr<Ipv6Addr>,
1229) {
1230    core_ctx.counters().tx.neighbor_advertisement.increment();
1231    debug!("send_neighbor_advertisement from {:?} to {:?}", device_addr, dst_ip);
1232    // We currently only allow the destination address to be:
1233    // 1) a unicast address.
1234    // 2) a multicast destination but the message should be an unsolicited
1235    //    neighbor advertisement.
1236    // NOTE: this assertion may need change if more messages are to be allowed
1237    // in the future.
1238    debug_assert!(dst_ip.is_valid_unicast() || (!solicited && dst_ip.is_multicast()));
1239
1240    // We must call into the higher level send_ip_packet_from_device function
1241    // because it is not guaranteed that we actually know the link-layer
1242    // address of the destination IP. Typically, the solicitation request will
1243    // carry that information, but it is not necessary. So it is perfectly valid
1244    // that trying to send this advertisement will end up triggering a neighbor
1245    // solicitation to be sent.
1246    let src_ll = core_ctx.get_link_layer_addr(&device_id);
1247
1248    // Nothing reasonable to do with the error.
1249    let advertisement = NeighborAdvertisement::new(
1250        core_ctx.is_router_device(&device_id),
1251        solicited,
1252        // Per RFC 4861, section 7.2.4:
1253        //
1254        //    If the Target Address is either an anycast address or a unicast
1255        //    address for which the node is providing proxy service, or the Target
1256        //    Link-Layer Address option is not included, the Override flag SHOULD
1257        //    be set to zero.  Otherwise, the Override flag SHOULD be set to one.
1258        //
1259        // We don't support anycast addresses or proxy ARP, and we only send neighbor
1260        // advertisements for addresses we own, so always set the Override flag.
1261        //
1262        // [RFC 4861, section 7.2.4]: https://tools.ietf.org/html/rfc4861#section-7.2.4
1263        true, /* override_flag */
1264        device_addr.get(),
1265    );
1266    let _: Result<(), _> = send_ndp_packet(
1267        core_ctx,
1268        bindings_ctx,
1269        &device_id,
1270        Some(device_addr.into_specified()),
1271        dst_ip,
1272        OptionSequenceBuilder::new(
1273            src_ll
1274                .as_ref()
1275                .map(Ipv6LinkLayerAddr::as_bytes)
1276                .map(NdpOptionBuilder::TargetLinkLayerAddress)
1277                .iter(),
1278        )
1279        .into_serializer(),
1280        NdpMessage::NeighborAdvertisement { message: advertisement, code: IcmpZeroCode },
1281    );
1282}
1283
1284fn receive_ndp_packet<
1285    B: SplitByteSlice,
1286    BC: IcmpBindingsContext + NdpBindingsContext<CC::DeviceId>,
1287    CC: InnerIcmpv6Context<BC>
1288        + Ipv6DeviceHandler<BC>
1289        + IpDeviceHandler<Ipv6, BC>
1290        + IpDeviceIngressStateContext<Ipv6>
1291        + NudIpHandler<Ipv6, BC>
1292        + IpLayerHandler<Ipv6, BC>
1293        + CounterContext<NdpCounters>,
1294    H: IpHeaderInfo<Ipv6>,
1295>(
1296    core_ctx: &mut CC,
1297    bindings_ctx: &mut BC,
1298    device_id: &CC::DeviceId,
1299    src_ip: Ipv6SourceAddr,
1300    dst_ip: SpecifiedAddr<Ipv6Addr>,
1301    packet: NdpPacket<B>,
1302    header_info: &H,
1303) {
1304    // All NDP messages should be dropped if the hop-limit != 255. See
1305    //   Router Solicitations: RFC 4861 section 6.1.1,
1306    //   Router Advertisements: RFC 4861 section 6.1.2,
1307    //   Neighbor Solicitations: RFC 4861 section 7.1.1,
1308    //   Neighbor Advertisements: RFC 4861 section 7.1.2, and
1309    //   Redirect: RFC 4861 section 8.1:
1310    //
1311    //       A node MUST silently discard any received [NDP Message Type]
1312    //       messages that do not satisfy all of the following validity
1313    //       checks:
1314    //
1315    //          ...
1316    //
1317    //          - The IP Hop Limit field has a value of 255, i.e., the packet
1318    //            could not possibly have been forwarded by a router.
1319    //
1320    //          ...
1321    if header_info.hop_limit() != REQUIRED_NDP_IP_PACKET_HOP_LIMIT {
1322        trace!("dropping NDP packet from {src_ip} with invalid hop limit");
1323        return;
1324    }
1325
1326    match packet {
1327        NdpPacket::RouterSolicitation(_) | NdpPacket::Redirect(_) => {}
1328        NdpPacket::NeighborSolicitation(ref p) => {
1329            // Per RFC 4861, section 7.1.1:
1330            //   A node MUST silently discard any received Neighbor Solicitation
1331            //   messages that do not satisfy all of the following validity
1332            //   checks:
1333            //   [...]
1334            //     - Target Address is not a multicast address.
1335            let target_address = p.message().target_address();
1336            let target_address = match UnicastAddr::new(*target_address) {
1337                Some(a) => a,
1338                None => {
1339                    trace!(
1340                        "dropping NS from {} with non-unicast target={:?}",
1341                        src_ip, target_address
1342                    );
1343                    return;
1344                }
1345            };
1346
1347            if src_ip == Ipv6SourceAddr::Unspecified {
1348                // Per RFC 4861, section 7.1.1:
1349                //   A node MUST silently discard any received Neighbor
1350                //   Solicitation messages that do not satisfy all of the
1351                //   following validity checks:
1352                //   [...]
1353                //     - If the IP source address is the unspecified
1354                //       address, the IP destination address is a
1355                //       solicited-node multicast address.
1356                if target_address.get().to_solicited_node_address().get() != dst_ip.get() {
1357                    debug!(
1358                        "dropping NS from {} for {} with invalid IPv6 dst ({}).",
1359                        src_ip, target_address, dst_ip
1360                    );
1361                    return;
1362                }
1363
1364                // TODO(https://fxbug.dev/454387514): Once the Source
1365                // Link-Layer Address NDP option is supported, we'll need to
1366                // check for its presence here. RFC 4861, section 7.1.1
1367                // states we should discard the Neighbor Solicitation if it
1368                // includes the option alongside an unspecified source.
1369            }
1370
1371            core_ctx.counters().rx.neighbor_solicitation.increment();
1372
1373            match src_ip {
1374                Ipv6SourceAddr::Unspecified => {
1375                    // The neighbor is performing Duplicate address detection.
1376                    //
1377                    // As per RFC 4861 section 4.3,
1378                    //
1379                    //   Source Address
1380                    //       Either an address assigned to the interface from
1381                    //       which this message is sent or (if Duplicate Address
1382                    //       Detection is in progress [ADDRCONF]) the
1383                    //       unspecified address.
1384                    match IpDeviceHandler::handle_received_dad_packet(
1385                        core_ctx,
1386                        bindings_ctx,
1387                        &device_id,
1388                        target_address.into_specified(),
1389                        p.body().iter().find_map(|option| option.nonce()),
1390                    ) {
1391                        Some(IpAddressState::Assigned) => {
1392                            // Address is assigned to us to we let the
1393                            // remote node performing DAD that we own the
1394                            // address.
1395                            send_neighbor_advertisement(
1396                                core_ctx,
1397                                bindings_ctx,
1398                                &device_id,
1399                                false,
1400                                target_address,
1401                                Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS.into_specified(),
1402                            );
1403                        }
1404                        Some(IpAddressState::Tentative) => {
1405                            // Nothing further to do in response to DAD
1406                            // messages.
1407                        }
1408                        Some(IpAddressState::Unavailable) | None => {
1409                            // Nothing further to do for unassigned target
1410                            // addresses.
1411                        }
1412                    }
1413
1414                    return;
1415                }
1416                Ipv6SourceAddr::Unicast(src_ip) => {
1417                    // Neighbor is performing link address resolution.
1418                    match core_ctx
1419                        .address_status_for_device(target_address.into_specified(), device_id)
1420                    {
1421                        AddressStatus::Present(Ipv6PresentAddressStatus::UnicastAssigned) => {}
1422                        AddressStatus::Present(
1423                            Ipv6PresentAddressStatus::UnicastTentative
1424                            | Ipv6PresentAddressStatus::Multicast,
1425                        )
1426                        | AddressStatus::Unassigned => {
1427                            // Address is not considered assigned to us as a
1428                            // unicast so don't send a neighbor advertisement
1429                            // reply.
1430                            return;
1431                        }
1432                    }
1433
1434                    let link_addr = p.body().iter().find_map(|o| o.source_link_layer_address());
1435
1436                    if let Some(link_addr) = link_addr {
1437                        NudIpHandler::handle_neighbor_probe(
1438                            core_ctx,
1439                            bindings_ctx,
1440                            &device_id,
1441                            src_ip.into_specified(),
1442                            link_addr,
1443                        );
1444                    }
1445
1446                    send_neighbor_advertisement(
1447                        core_ctx,
1448                        bindings_ctx,
1449                        &device_id,
1450                        true,
1451                        target_address,
1452                        src_ip.into_specified(),
1453                    );
1454                }
1455            }
1456        }
1457        NdpPacket::NeighborAdvertisement(ref p) => {
1458            // TODO(https://fxbug.dev/42179526): Invalidate discovered routers when
1459            // neighbor entry's IsRouter field transitions to false.
1460
1461            let target_address = p.message().target_address();
1462
1463            let src_ip = match src_ip {
1464                Ipv6SourceAddr::Unicast(src_ip) => src_ip,
1465                Ipv6SourceAddr::Unspecified => {
1466                    trace!("dropping NA with unspecified source and target = {:?}", target_address);
1467                    return;
1468                }
1469            };
1470
1471            let target_address = match UnicastAddr::new(*target_address) {
1472                Some(a) => a,
1473                None => {
1474                    trace!(
1475                        "dropping NA from {} with non-unicast target={:?}",
1476                        src_ip, target_address
1477                    );
1478                    return;
1479                }
1480            };
1481
1482            core_ctx.counters().rx.neighbor_advertisement.increment();
1483
1484            // Note: Neighbor Advertisements don't carry a nonce value. Handle
1485            // a NA in the same way that we would handle an NS that omitted the
1486            // nonce (i.e. conclude it's not-looped-back).
1487            let nonce = None;
1488            match IpDeviceHandler::handle_received_dad_packet(
1489                core_ctx,
1490                bindings_ctx,
1491                &device_id,
1492                target_address.into_specified(),
1493                nonce,
1494            ) {
1495                Some(IpAddressState::Assigned) => {
1496                    // A neighbor is advertising that it owns an address
1497                    // that we also have assigned. This is out of scope
1498                    // for DAD.
1499                    //
1500                    // As per RFC 4862 section 5.4.4,
1501                    //
1502                    //   2.  If the target address matches a unicast address
1503                    //       assigned to the receiving interface, it would
1504                    //       possibly indicate that the address is a
1505                    //       duplicate but it has not been detected by the
1506                    //       Duplicate Address Detection procedure (recall
1507                    //       that Duplicate Address Detection is not
1508                    //       completely reliable). How to handle such a case
1509                    //       is beyond the scope of this document.
1510                    //
1511                    // TODO(https://fxbug.dev/42111744): Signal to bindings
1512                    // that a duplicate address is detected.
1513                    error!(
1514                        "NA from {src_ip} with target address {target_address} that is also \
1515                        assigned on device {device_id:?}",
1516                    );
1517                }
1518                Some(IpAddressState::Tentative) => {
1519                    // Nothing further to do for an NA from a neighbor that
1520                    // targets an address we also have assigned.
1521                    return;
1522                }
1523                Some(IpAddressState::Unavailable) | None => {
1524                    // Address not targeting us so we know its for a neighbor.
1525                    //
1526                    // TODO(https://fxbug.dev/42182317): Move NUD to IP.
1527                }
1528            }
1529
1530            let link_addr = p.body().iter().find_map(|o| o.target_link_layer_address());
1531
1532            NudIpHandler::handle_neighbor_confirmation(
1533                core_ctx,
1534                bindings_ctx,
1535                &device_id,
1536                target_address.into_specified(),
1537                link_addr,
1538                ConfirmationFlags {
1539                    solicited_flag: p.message().solicited_flag(),
1540                    override_flag: p.message().override_flag(),
1541                },
1542            );
1543        }
1544        NdpPacket::RouterAdvertisement(ref p) => {
1545            // As per RFC 4861 section 6.1.2,
1546            //
1547            //   A node MUST silently discard any received Router Advertisement
1548            //   messages that do not satisfy all of the following validity
1549            //   checks:
1550            //
1551            //      - IP Source Address is a link-local address.  Routers must
1552            //        use their link-local address as the source for Router
1553            //        Advertisement and Redirect messages so that hosts can
1554            //        uniquely identify routers.
1555            //
1556            //        ...
1557            let src_ip = match src_ip {
1558                Ipv6SourceAddr::Unicast(ip) => match LinkLocalUnicastAddr::new(*ip) {
1559                    Some(ip) => ip,
1560                    None => return,
1561                },
1562                Ipv6SourceAddr::Unspecified => return,
1563            };
1564
1565            let ra = p.message();
1566            debug!("received router advertisement from {:?}: {:?}", src_ip, ra);
1567            core_ctx.counters().rx.router_advertisement.increment();
1568
1569            // As per RFC 4861 section 6.3.4,
1570            //   The RetransTimer variable SHOULD be copied from the Retrans
1571            //   Timer field, if it is specified.
1572            //
1573            // TODO(https://fxbug.dev/42052173): Control whether or not we should
1574            // update the retransmit timer.
1575            if let Some(retransmit_timer) = ra.retransmit_timer() {
1576                Ipv6DeviceHandler::set_discovered_retrans_timer(
1577                    core_ctx,
1578                    bindings_ctx,
1579                    &device_id,
1580                    retransmit_timer,
1581                );
1582            }
1583
1584            // As per RFC 4861 section 6.3.4:
1585            //   If the received Cur Hop Limit value is specified, the host
1586            //   SHOULD set its CurHopLimit variable to the received value.
1587            //
1588            // TODO(https://fxbug.dev/42052173): Control whether or not we should
1589            // update the default hop limit.
1590            if let Some(hop_limit) = ra.current_hop_limit() {
1591                trace!(
1592                    "receive_ndp_packet: NDP RA: updating device's hop limit to {:?} for router: {:?}",
1593                    ra.current_hop_limit(),
1594                    src_ip
1595                );
1596                IpDeviceHandler::set_default_hop_limit(core_ctx, &device_id, hop_limit);
1597            }
1598
1599            // TODO(https://fxbug.dev/42077316): Support default router preference.
1600            Ipv6DeviceHandler::update_discovered_ipv6_route(
1601                core_ctx,
1602                bindings_ctx,
1603                &device_id,
1604                Ipv6DiscoveredRoute { subnet: IPV6_DEFAULT_SUBNET, gateway: Some(src_ip) },
1605                p.message().router_lifetime().map(NonZeroNdpLifetime::Finite),
1606            );
1607
1608            for option in p.body().iter() {
1609                match option {
1610                    NdpOption::TargetLinkLayerAddress(_)
1611                    | NdpOption::RedirectedHeader { .. }
1612                    | NdpOption::RecursiveDnsServer(_)
1613                    | NdpOption::Nonce(_) => {}
1614                    NdpOption::SourceLinkLayerAddress(addr) => {
1615                        debug!("processing SourceLinkLayerAddress option in RA: {:?}", addr);
1616                        // As per RFC 4861 section 6.3.4,
1617                        //
1618                        //   If the advertisement contains a Source Link-Layer
1619                        //   Address option, the link-layer address SHOULD be
1620                        //   recorded in the Neighbor Cache entry for the router
1621                        //   (creating an entry if necessary) and the IsRouter
1622                        //   flag in the Neighbor Cache entry MUST be set to
1623                        //   TRUE. If no Source Link-Layer Address is included,
1624                        //   but a corresponding Neighbor Cache entry exists,
1625                        //   its IsRouter flag MUST be set to TRUE. The IsRouter
1626                        //   flag is used by Neighbor Unreachability Detection
1627                        //   to determine when a router changes to being a host
1628                        //   (i.e., no longer capable of forwarding packets).
1629                        //   If a Neighbor Cache entry is created for the
1630                        //   router, its reachability state MUST be set to STALE
1631                        //   as specified in Section 7.3.3.  If a cache entry
1632                        //   already exists and is updated with a different
1633                        //   link-layer address, the reachability state MUST
1634                        //   also be set to STALE.if a Neighbor Cache entry
1635                        //
1636                        // We do not yet support NUD as described in RFC 4861
1637                        // so for now we just record the link-layer address in
1638                        // our neighbor table.
1639                        //
1640                        // TODO(https://fxbug.dev/42083367): Add support for routers in NUD.
1641                        NudIpHandler::handle_neighbor_probe(
1642                            core_ctx,
1643                            bindings_ctx,
1644                            &device_id,
1645                            {
1646                                let src_ip: UnicastAddr<_> = src_ip.into_addr();
1647                                src_ip.into_specified()
1648                            },
1649                            addr,
1650                        );
1651                    }
1652                    NdpOption::PrefixInformation(prefix_info) => {
1653                        debug!("processing Prefix Information option in RA: {:?}", prefix_info);
1654                        // As per RFC 4861 section 6.3.4,
1655                        //
1656                        //   For each Prefix Information option with the on-link
1657                        //   flag set, a host does the following:
1658                        //
1659                        //      - If the prefix is the link-local prefix,
1660                        //        silently ignore the Prefix Information option.
1661                        //
1662                        // Also as per RFC 4862 section 5.5.3,
1663                        //
1664                        //   For each Prefix-Information option in the Router
1665                        //   Advertisement:
1666                        //
1667                        //    ..
1668                        //
1669                        //    b)  If the prefix is the link-local prefix,
1670                        //        silently ignore the Prefix Information option.
1671                        if prefix_info.prefix().is_link_local() {
1672                            continue;
1673                        }
1674
1675                        let subnet = match prefix_info.subnet() {
1676                            Ok(subnet) => subnet,
1677                            Err(err) => match err {
1678                                SubnetError::PrefixTooLong | SubnetError::HostBitsSet => continue,
1679                            },
1680                        };
1681
1682                        match UnicastAddr::new(subnet.network()) {
1683                            Some(UnicastAddr { .. }) => {}
1684                            None => continue,
1685                        }
1686
1687                        let valid_lifetime = prefix_info.valid_lifetime();
1688
1689                        if prefix_info.on_link_flag() {
1690                            // TODO(https://fxbug.dev/42077316): Support route preference.
1691                            Ipv6DeviceHandler::update_discovered_ipv6_route(
1692                                core_ctx,
1693                                bindings_ctx,
1694                                &device_id,
1695                                Ipv6DiscoveredRoute { subnet, gateway: None },
1696                                valid_lifetime,
1697                            )
1698                        }
1699
1700                        if prefix_info.autonomous_address_configuration_flag() {
1701                            Ipv6DeviceHandler::apply_slaac_update(
1702                                core_ctx,
1703                                bindings_ctx,
1704                                &device_id,
1705                                subnet,
1706                                prefix_info.preferred_lifetime(),
1707                                valid_lifetime,
1708                            );
1709                        }
1710                    }
1711                    NdpOption::RouteInformation(rio) => {
1712                        debug!("processing Route Information option in RA: {:?}", rio);
1713                        // TODO(https://fxbug.dev/42077316): Support route preference.
1714                        Ipv6DeviceHandler::update_discovered_ipv6_route(
1715                            core_ctx,
1716                            bindings_ctx,
1717                            &device_id,
1718                            Ipv6DiscoveredRoute {
1719                                subnet: rio.prefix().clone(),
1720                                gateway: Some(src_ip),
1721                            },
1722                            rio.route_lifetime(),
1723                        )
1724                    }
1725                    NdpOption::Mtu(mtu) => {
1726                        debug!("processing MTU option in RA: {:?}", mtu);
1727                        // TODO(https://fxbug.dev/42052173): Control whether or
1728                        // not we should update the link's MTU in response to
1729                        // RAs.
1730                        Ipv6DeviceHandler::set_link_mtu(core_ctx, &device_id, Mtu::new(mtu));
1731                    }
1732                }
1733            }
1734
1735            bindings_ctx.on_event(RouterAdvertisementEvent {
1736                options_bytes: Box::from(p.body().bytes()),
1737                source: **src_ip,
1738                device: device_id.clone(),
1739            });
1740        }
1741    }
1742}
1743
1744impl<
1745    BC: IcmpBindingsContext + NdpBindingsContext<CC::DeviceId>,
1746    CC: InnerIcmpv6Context<BC>
1747        + InnerIcmpContext<Ipv6, BC>
1748        + Ipv6DeviceHandler<BC>
1749        + IpDeviceHandler<Ipv6, BC>
1750        + IpDeviceIngressStateContext<Ipv6>
1751        + PmtuHandler<Ipv6, BC>
1752        + NudIpHandler<Ipv6, BC>
1753        + IpLayerHandler<Ipv6, BC>
1754        + CounterContext<IcmpRxCounters<Ipv6>>
1755        + CounterContext<IcmpTxCounters<Ipv6>>
1756        + CounterContext<NdpCounters>,
1757> IpTransportContext<Ipv6, BC, CC> for IcmpIpTransportContext
1758{
1759    fn receive_icmp_error(
1760        core_ctx: &mut CC,
1761        bindings_ctx: &mut BC,
1762        device: &CC::DeviceId,
1763        original_src_ip: Option<SpecifiedAddr<Ipv6Addr>>,
1764        original_dst_ip: SpecifiedAddr<Ipv6Addr>,
1765        original_body: &[u8],
1766        err: Icmpv6ErrorCode,
1767    ) {
1768        receive_ip_transport_icmp_error(
1769            core_ctx,
1770            bindings_ctx,
1771            device,
1772            original_src_ip,
1773            original_dst_ip,
1774            original_body,
1775            err,
1776        )
1777    }
1778
1779    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<Ipv6>>(
1780        core_ctx: &mut CC,
1781        bindings_ctx: &mut BC,
1782        device: &CC::DeviceId,
1783        src_ip: Ipv6SourceAddr,
1784        dst_ip: SpecifiedAddr<Ipv6Addr>,
1785        mut buffer: B,
1786        info: &LocalDeliveryPacketInfo<Ipv6, H>,
1787    ) -> Result<(), (B, TransportReceiveError)> {
1788        let LocalDeliveryPacketInfo { meta, header_info, marks } = info;
1789        let ReceiveIpPacketMeta { broadcast: _, transparent_override } = meta;
1790        if let Some(delivery) = transparent_override {
1791            unreachable!(
1792                "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1793                transparent proxy rules can only be configured for TCP and UDP packets"
1794            );
1795        }
1796
1797        trace!(
1798            "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet({:?}, {})",
1799            src_ip, dst_ip
1800        );
1801
1802        let packet = match buffer
1803            .parse_with::<_, Icmpv6Packet<_>>(IcmpParseArgs::new(src_ip.get(), dst_ip))
1804        {
1805            Ok(packet) => packet,
1806            Err(_) => return Ok(()), // TODO(joshlf): Do something else here?
1807        };
1808
1809        match packet {
1810            Icmpv6Packet::EchoRequest(echo_request) => {
1811                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx).echo_request.increment();
1812
1813                if let Some(src_ip) = SocketIpAddr::new_from_ipv6_source(src_ip) {
1814                    match SocketIpAddr::try_from(dst_ip) {
1815                        Ok(dst_ip) => {
1816                            let req = *echo_request.message();
1817                            let code = echo_request.code();
1818                            let (local_ip, remote_ip) = (dst_ip, src_ip);
1819                            debug!(
1820                                "replying to ICMP echo request from {remote_ip}: id={}, seq={}",
1821                                req.id(),
1822                                req.seq()
1823                            );
1824                            send_icmp_reply(
1825                                core_ctx,
1826                                bindings_ctx,
1827                                device,
1828                                remote_ip,
1829                                local_ip,
1830                                |src_ip| {
1831                                    IcmpPacketBuilder::<Ipv6, _>::new(
1832                                        src_ip,
1833                                        remote_ip.addr(),
1834                                        code,
1835                                        req.reply(),
1836                                    )
1837                                    .wrap_body(buffer)
1838                                },
1839                                &WithMarks(marks),
1840                            );
1841                        }
1842                        Err(AddrIsMappedError {}) => {
1843                            trace!(
1844                                "IpTransportContext<Ipv6>::receive_ip_packet: Received echo request with an ipv4-mapped-ipv6 destination address"
1845                            );
1846                        }
1847                    }
1848                } else {
1849                    trace!(
1850                        "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received echo request with an unspecified source address"
1851                    );
1852                }
1853            }
1854            Icmpv6Packet::EchoReply(echo_reply) => {
1855                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx).echo_reply.increment();
1856                trace!(
1857                    "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received an EchoReply message"
1858                );
1859                let parse_metadata = echo_reply.parse_metadata();
1860                buffer.undo_parse(parse_metadata);
1861                return <CC::EchoTransportContext
1862                            as IpTransportContext<Ipv6, BC, CC>>::receive_ip_packet(
1863                        core_ctx,
1864                        bindings_ctx,
1865                        device,
1866                        src_ip,
1867                        dst_ip,
1868                        buffer,
1869                        info
1870                );
1871            }
1872            Icmpv6Packet::Ndp(packet) => receive_ndp_packet(
1873                core_ctx,
1874                bindings_ctx,
1875                device,
1876                src_ip,
1877                dst_ip,
1878                packet,
1879                header_info,
1880            ),
1881            Icmpv6Packet::PacketTooBig(packet_too_big) => {
1882                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx)
1883                    .packet_too_big
1884                    .increment();
1885                trace!(
1886                    "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received a Packet Too Big message"
1887                );
1888                if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
1889                    // We are updating the path MTU from the destination address
1890                    // of this `packet` (which is an IP address on this node) to
1891                    // some remote (identified by the source address of this
1892                    // `packet`).
1893                    //
1894                    // `update_pmtu_if_less` will only update the PMTU if the
1895                    // Packet Too Big message's MTU field had a value that was
1896                    // at least the IPv6 minimum MTU (which is required by IPv6
1897                    // RFC 8200).
1898                    let mtu = core_ctx.update_pmtu_if_less(
1899                        bindings_ctx,
1900                        dst_ip.get(),
1901                        src_ip.get(),
1902                        Mtu::new(packet_too_big.message().mtu()),
1903                    );
1904                    if let Some(mtu) = mtu {
1905                        receive_icmpv6_error(
1906                            core_ctx,
1907                            bindings_ctx,
1908                            device,
1909                            &packet_too_big,
1910                            Icmpv6ErrorCode::PacketTooBig(mtu),
1911                        );
1912                    }
1913                }
1914            }
1915            Icmpv6Packet::Mld(packet) => {
1916                core_ctx.receive_mld_packet(
1917                    bindings_ctx,
1918                    &device,
1919                    src_ip,
1920                    dst_ip,
1921                    packet,
1922                    header_info,
1923                );
1924            }
1925            Icmpv6Packet::DestUnreachable(dest_unreachable) => receive_icmpv6_error(
1926                core_ctx,
1927                bindings_ctx,
1928                device,
1929                &dest_unreachable,
1930                Icmpv6ErrorCode::DestUnreachable(dest_unreachable.code()),
1931            ),
1932            Icmpv6Packet::TimeExceeded(time_exceeded) => receive_icmpv6_error(
1933                core_ctx,
1934                bindings_ctx,
1935                device,
1936                &time_exceeded,
1937                Icmpv6ErrorCode::TimeExceeded(time_exceeded.code()),
1938            ),
1939            Icmpv6Packet::ParameterProblem(parameter_problem) => receive_icmpv6_error(
1940                core_ctx,
1941                bindings_ctx,
1942                device,
1943                &parameter_problem,
1944                Icmpv6ErrorCode::ParameterProblem(parameter_problem.code()),
1945            ),
1946        }
1947
1948        Ok(())
1949    }
1950}
1951
1952#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1953struct WithMarks<'a>(&'a Marks);
1954
1955impl<'a> OptionDelegationMarker for WithMarks<'a> {}
1956
1957impl<'a, I: IpExt> DelegatedRouteResolutionOptions<I> for WithMarks<'a> {
1958    fn marks(&self) -> &Marks {
1959        let Self(marks) = self;
1960        marks
1961    }
1962}
1963
1964impl<'a, I: IpExt> DelegatedSendOptions<I> for WithMarks<'a> {}
1965
1966/// Sends an ICMP reply to a remote host.
1967///
1968/// `send_icmp_reply` sends a reply to a non-error message (e.g., "echo request"
1969/// or "timestamp request" messages).
1970///
1971/// `get_body_from_src_ip` returns a `Serializer` with the bytes of the ICMP
1972/// packet, and, when called, is given the source IP address chosen for the
1973/// outbound packet. This allows `get_body_from_src_ip` to properly compute the
1974/// ICMP checksum, which relies on both the source and destination IP addresses
1975/// of the IP packet it's encapsulated in.
1976fn send_icmp_reply<I, BC, CC, S, F, O>(
1977    core_ctx: &mut CC,
1978    bindings_ctx: &mut BC,
1979    device: &CC::DeviceId,
1980    original_src_ip: SocketIpAddr<I::Addr>,
1981    original_dst_ip: SocketIpAddr<I::Addr>,
1982    get_body_from_src_ip: F,
1983    ip_options: &O,
1984) where
1985    I: IpExt + FilterIpExt,
1986    CC: IpSocketHandler<I, BC> + DeviceIdContext<AnyDevice> + CounterContext<IcmpTxCounters<I>>,
1987    BC: TxMetadataBindingsTypes,
1988    S: DynamicTransportSerializer<I>,
1989    F: FnOnce(SpecifiedAddr<I::Addr>) -> S,
1990    O: SendOptions<I> + RouteResolutionOptions<I>,
1991{
1992    trace!("send_icmp_reply({:?}, {}, {})", device, original_src_ip, original_dst_ip);
1993    core_ctx.counters().reply.increment();
1994    let tx_metadata: BC::TxMetadata = Default::default();
1995
1996    // Force the egress device if the original destination is multicast or
1997    // requires a zone (i.e. link-local non-loopback), ensuring we pick the
1998    // correct return route.
1999    let egress_device = (original_dst_ip.as_ref().is_multicast()
2000        || original_dst_ip.as_ref().must_have_zone())
2001    .then_some(EitherDeviceId::Strong(device));
2002
2003    core_ctx
2004        .send_oneshot_ip_packet_with_dyn_serializer(
2005            bindings_ctx,
2006            IpSocketArgs {
2007                device: egress_device,
2008                local_ip: IpDeviceAddr::new_from_socket_ip_addr(original_dst_ip),
2009                remote_ip: original_src_ip,
2010                proto: I::ICMP_IP_PROTO,
2011                options: ip_options,
2012            },
2013            tx_metadata,
2014            |src_ip| get_body_from_src_ip(src_ip.into()),
2015        )
2016        .unwrap_or_else(|err| {
2017            debug!("failed to send ICMP reply: {}", err);
2018        })
2019}
2020
2021/// Receive an ICMP(v4) error message.
2022///
2023/// `receive_icmpv4_error` handles an incoming ICMP error message by parsing the
2024/// original IPv4 packet and then delegating to the context.
2025fn receive_icmpv4_error<
2026    BC: IcmpBindingsContext,
2027    CC: InnerIcmpv4Context<BC>,
2028    B: SplitByteSlice,
2029    M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>,
2030>(
2031    core_ctx: &mut CC,
2032    bindings_ctx: &mut BC,
2033    device: &CC::DeviceId,
2034    packet: &IcmpPacket<Ipv4, B, M>,
2035    err: Icmpv4ErrorCode,
2036) {
2037    packet.with_original_packet(|res| match res {
2038        Ok(original_packet) => {
2039            let dst_ip = match SpecifiedAddr::new(original_packet.dst_ip()) {
2040                Some(ip) => ip,
2041                None => {
2042                    trace!("receive_icmpv4_error: Got ICMP error message whose original IPv4 packet contains an unspecified destination address; discarding");
2043                    return;
2044                },
2045            };
2046            InnerIcmpContext::receive_icmp_error(
2047                core_ctx,
2048                bindings_ctx,
2049                device,
2050                SpecifiedAddr::new(original_packet.src_ip()),
2051                dst_ip,
2052                original_packet.proto(),
2053                original_packet.body().into_inner(),
2054                err,
2055            );
2056        }
2057        Err(_) => debug!(
2058            "receive_icmpv4_error: Got ICMP error message with unparsable original IPv4 packet"
2059        ),
2060    })
2061}
2062
2063/// Receive an ICMPv6 error message.
2064///
2065/// `receive_icmpv6_error` handles an incoming ICMPv6 error message by parsing
2066/// the original IPv6 packet and then delegating to the context.
2067fn receive_icmpv6_error<
2068    BC: IcmpBindingsContext,
2069    CC: InnerIcmpv6Context<BC>,
2070    B: SplitByteSlice,
2071    M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>,
2072>(
2073    core_ctx: &mut CC,
2074    bindings_ctx: &mut BC,
2075    device: &CC::DeviceId,
2076    packet: &IcmpPacket<Ipv6, B, M>,
2077    err: Icmpv6ErrorCode,
2078) {
2079    packet.with_original_packet(|res| match res {
2080        Ok(original_packet) => {
2081            let dst_ip = match SpecifiedAddr::new(original_packet.dst_ip()) {
2082                Some(ip)=>ip,
2083                None => {
2084                    trace!("receive_icmpv6_error: Got ICMP error message whose original IPv6 packet contains an unspecified destination address; discarding");
2085                    return;
2086                },
2087            };
2088            match original_packet.body_proto() {
2089                Ok((body, proto)) => {
2090                    InnerIcmpContext::receive_icmp_error(
2091                        core_ctx,
2092                        bindings_ctx,
2093                        device,
2094                        SpecifiedAddr::new(original_packet.src_ip()),
2095                        dst_ip,
2096                        proto,
2097                        body.into_inner(),
2098                        err,
2099                    );
2100                }
2101                Err(ExtHdrParseError) => {
2102                    trace!("receive_icmpv6_error: We could not parse the original packet's extension headers, and so we don't know where the original packet's body begins; discarding");
2103                    // There's nothing we can do in this case, so we just
2104                    // return.
2105                    return;
2106                }
2107            }
2108        }
2109        Err(_body) => debug!(
2110            "receive_icmpv6_error: Got ICMPv6 error message with unparsable original IPv6 packet"
2111        ),
2112    })
2113}
2114
2115/// Send an ICMP(v4) message in response to receiving a packet destined for an
2116/// unreachable address.
2117///
2118/// `send_icmpv4_host_unreachable` sends the appropriate ICMP message in
2119/// response to receiving an IP packet from `src_ip` to `dst_ip`, where
2120/// `dst_ip` is unreachable. In particular, this is an ICMP
2121/// "destination unreachable" message with a "host unreachable" code.
2122///
2123/// `original_packet` must be an initial fragment or a complete IP
2124/// packet, per [RFC 792 Introduction]:
2125///
2126///   Also ICMP messages are only sent about errors in handling fragment zero of
2127///   fragemented [sic] datagrams.
2128///
2129/// `header_len` is the length of the header including all options.
2130///
2131/// [RFC 792 Introduction]: https://datatracker.ietf.org/doc/html/rfc792
2132pub fn send_icmpv4_host_unreachable<
2133    B: BufferMut,
2134    BC: IcmpBindingsContext,
2135    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2136>(
2137    core_ctx: &mut CC,
2138    bindings_ctx: &mut BC,
2139    device: Option<&CC::DeviceId>,
2140    frame_dst: Option<FrameDestination>,
2141    src_ip: SocketIpAddr<Ipv4Addr>,
2142    dst_ip: SocketIpAddr<Ipv4Addr>,
2143    original_packet: B,
2144    header_len: usize,
2145    fragment_type: Ipv4FragmentType,
2146    marks: &Marks,
2147) {
2148    core_ctx.counters().address_unreachable.increment();
2149
2150    send_icmpv4_dest_unreachable(
2151        core_ctx,
2152        bindings_ctx,
2153        device,
2154        frame_dst,
2155        src_ip,
2156        dst_ip,
2157        Icmpv4DestUnreachableCode::DestHostUnreachable,
2158        original_packet,
2159        header_len,
2160        fragment_type,
2161        marks,
2162    );
2163}
2164
2165/// Send an ICMPv6 message in response to receiving a packet destined for an
2166/// unreachable address.
2167///
2168/// `send_icmpv6_address_unreachable` sends the appropriate ICMP message in
2169/// response to receiving an IP packet from `src_ip` to `dst_ip`, where
2170/// `dst_ip` is unreachable. In particular, this is an ICMP
2171/// "destination unreachable" message with an "address unreachable" code.
2172///
2173/// `original_packet` contains the contents of the entire original packet,
2174/// including extension headers.
2175pub fn send_icmpv6_address_unreachable<
2176    B: BufferMut,
2177    BC: IcmpBindingsContext,
2178    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2179>(
2180    core_ctx: &mut CC,
2181    bindings_ctx: &mut BC,
2182    device: Option<&CC::DeviceId>,
2183    frame_dst: Option<FrameDestination>,
2184    src_ip: SocketIpAddr<Ipv6Addr>,
2185    dst_ip: SocketIpAddr<Ipv6Addr>,
2186    original_packet: B,
2187    marks: &Marks,
2188) {
2189    core_ctx.counters().address_unreachable.increment();
2190
2191    send_icmpv6_dest_unreachable(
2192        core_ctx,
2193        bindings_ctx,
2194        device,
2195        frame_dst,
2196        src_ip,
2197        dst_ip,
2198        Icmpv6DestUnreachableCode::AddrUnreachable,
2199        original_packet,
2200        marks,
2201    );
2202}
2203
2204/// Send an ICMP(v4) message in response to receiving a packet destined for an
2205/// unsupported IPv4 protocol.
2206///
2207/// `send_icmpv4_protocol_unreachable` sends the appropriate ICMP message in
2208/// response to receiving an IP packet from `src_ip` to `dst_ip` identifying an
2209/// unsupported protocol - in particular, a "destination unreachable" message
2210/// with a "protocol unreachable" code.
2211///
2212/// `original_packet` contains the contents of the entire original packet,
2213/// including the IP header. This must be a whole packet, not a packet fragment.
2214/// `header_len` is the length of the header including all options.
2215pub(crate) fn send_icmpv4_protocol_unreachable<
2216    B: BufferMut,
2217    BC: IcmpBindingsContext,
2218    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2219>(
2220    core_ctx: &mut CC,
2221    bindings_ctx: &mut BC,
2222    device: &CC::DeviceId,
2223    frame_dst: Option<FrameDestination>,
2224    src_ip: SocketIpAddr<Ipv4Addr>,
2225    dst_ip: SocketIpAddr<Ipv4Addr>,
2226    original_packet: B,
2227    header_len: usize,
2228    marks: &Marks,
2229) {
2230    core_ctx.counters().protocol_unreachable.increment();
2231
2232    send_icmpv4_dest_unreachable(
2233        core_ctx,
2234        bindings_ctx,
2235        Some(device),
2236        frame_dst,
2237        src_ip,
2238        dst_ip,
2239        Icmpv4DestUnreachableCode::DestProtocolUnreachable,
2240        original_packet,
2241        header_len,
2242        // If we are sending a protocol unreachable error it is correct to assume that, if the
2243        // packet was initially fragmented, it has been successfully reassembled by now. It
2244        // guarantees that we won't send more than one ICMP Destination Unreachable message for
2245        // different fragments of the same original packet, so we should behave as if we are
2246        // handling an initial fragment.
2247        Ipv4FragmentType::InitialFragment,
2248        marks,
2249    );
2250}
2251
2252/// Send an ICMPv6 message in response to receiving a packet destined for an
2253/// unsupported Next Header.
2254///
2255/// `send_icmpv6_protocol_unreachable` is like
2256/// [`send_icmpv4_protocol_unreachable`], but for ICMPv6. It sends an ICMPv6
2257/// "parameter problem" message with an "unrecognized next header type" code.
2258///
2259/// `header_len` is the length of all IPv6 headers (including extension headers)
2260/// *before* the payload with the problematic Next Header type.
2261pub(crate) fn send_icmpv6_protocol_unreachable<
2262    B: BufferMut,
2263    BC: IcmpBindingsContext,
2264    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2265>(
2266    core_ctx: &mut CC,
2267    bindings_ctx: &mut BC,
2268    device: &CC::DeviceId,
2269    frame_dst: Option<FrameDestination>,
2270    src_ip: SocketIpAddr<Ipv6Addr>,
2271    dst_ip: SocketIpAddr<Ipv6Addr>,
2272    original_packet: B,
2273    header_len: usize,
2274    marks: &Marks,
2275) {
2276    core_ctx.counters().protocol_unreachable.increment();
2277
2278    send_icmpv6_parameter_problem(
2279        core_ctx,
2280        bindings_ctx,
2281        device,
2282        frame_dst,
2283        src_ip,
2284        dst_ip,
2285        Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
2286        // Per RFC 4443, the pointer refers to the first byte of the packet
2287        // whose Next Header field was unrecognized. It is measured as an offset
2288        // from the beginning of the first IPv6 header. E.g., a pointer of 40
2289        // (the length of a single IPv6 header) would indicate that the Next
2290        // Header field from that header - and hence of the first encapsulated
2291        // packet - was unrecognized.
2292        //
2293        // NOTE: Since header_len is a usize, this could theoretically be a
2294        // lossy conversion. However, all that means in practice is that, if a
2295        // remote host somehow managed to get us to process a frame with a 4GB
2296        // IP header and send an ICMP response, the pointer value would be
2297        // wrong. It's not worth wasting special logic to avoid generating a
2298        // malformed packet in a case that will almost certainly never happen.
2299        Icmpv6ParameterProblem::new(header_len as u32),
2300        original_packet,
2301        false,
2302        marks,
2303    );
2304}
2305
2306/// Send an ICMP(v4) message in response to receiving a packet destined for an
2307/// unreachable local transport-layer port.
2308///
2309/// `send_icmpv4_port_unreachable` sends the appropriate ICMP message in
2310/// response to receiving an IP packet from `src_ip` to `dst_ip` with an
2311/// unreachable local transport-layer port. In particular, this is an ICMP
2312/// "destination unreachable" message with a "port unreachable" code.
2313///
2314/// `original_packet` contains the contents of the entire original packet,
2315/// including the IP header. This must be a whole packet, not a packet fragment.
2316/// `header_len` is the length of the header including all options.
2317pub(crate) fn send_icmpv4_port_unreachable<
2318    B: BufferMut,
2319    BC: IcmpBindingsContext,
2320    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2321>(
2322    core_ctx: &mut CC,
2323    bindings_ctx: &mut BC,
2324    device: &CC::DeviceId,
2325    frame_dst: Option<FrameDestination>,
2326    src_ip: SocketIpAddr<Ipv4Addr>,
2327    dst_ip: SocketIpAddr<Ipv4Addr>,
2328    original_packet: B,
2329    header_len: usize,
2330    marks: &Marks,
2331) {
2332    core_ctx.counters().port_unreachable.increment();
2333
2334    send_icmpv4_dest_unreachable(
2335        core_ctx,
2336        bindings_ctx,
2337        Some(device),
2338        frame_dst,
2339        src_ip,
2340        dst_ip,
2341        Icmpv4DestUnreachableCode::DestPortUnreachable,
2342        original_packet,
2343        header_len,
2344        // If we are sending a port unreachable error it is correct to assume that, if the packet
2345        // was initially fragmented, it has been successfully reassembled by now. It guarantees that
2346        // we won't send more than one ICMP Destination Unreachable message for different fragments
2347        // of the same original packet, so we should behave as if we are handling an initial
2348        // fragment.
2349        Ipv4FragmentType::InitialFragment,
2350        marks,
2351    );
2352}
2353
2354/// Send an ICMPv6 message in response to receiving a packet destined for an
2355/// unreachable local transport-layer port.
2356///
2357/// `send_icmpv6_port_unreachable` is like [`send_icmpv4_port_unreachable`], but
2358/// for ICMPv6.
2359///
2360/// `original_packet` contains the contents of the entire original packet,
2361/// including extension headers.
2362pub(crate) fn send_icmpv6_port_unreachable<
2363    B: BufferMut,
2364    BC: IcmpBindingsContext,
2365    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2366>(
2367    core_ctx: &mut CC,
2368    bindings_ctx: &mut BC,
2369    device: &CC::DeviceId,
2370    frame_dst: Option<FrameDestination>,
2371    src_ip: SocketIpAddr<Ipv6Addr>,
2372    dst_ip: SocketIpAddr<Ipv6Addr>,
2373    original_packet: B,
2374    marks: &Marks,
2375) {
2376    core_ctx.counters().port_unreachable.increment();
2377
2378    send_icmpv6_dest_unreachable(
2379        core_ctx,
2380        bindings_ctx,
2381        Some(device),
2382        frame_dst,
2383        src_ip,
2384        dst_ip,
2385        Icmpv6DestUnreachableCode::PortUnreachable,
2386        original_packet,
2387        marks,
2388    );
2389}
2390
2391/// Send an ICMP(v4) message in response to receiving a packet destined for an
2392/// unreachable network.
2393///
2394/// `send_icmpv4_net_unreachable` sends the appropriate ICMP message in response
2395/// to receiving an IP packet from `src_ip` to an unreachable `dst_ip`. In
2396/// particular, this is an ICMP "destination unreachable" message with a "net
2397/// unreachable" code.
2398///
2399/// `original_packet` contains the contents of the entire original packet -
2400/// including all IP headers. `header_len` is the length of the IPv4 header. It
2401/// is ignored for IPv6.
2402pub(crate) fn send_icmpv4_net_unreachable<
2403    B: BufferMut,
2404    BC: IcmpBindingsContext,
2405    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2406>(
2407    core_ctx: &mut CC,
2408    bindings_ctx: &mut BC,
2409    device: &CC::DeviceId,
2410    frame_dst: Option<FrameDestination>,
2411    src_ip: SocketIpAddr<Ipv4Addr>,
2412    dst_ip: SocketIpAddr<Ipv4Addr>,
2413    proto: Ipv4Proto,
2414    original_packet: B,
2415    header_len: usize,
2416    fragment_type: Ipv4FragmentType,
2417    marks: &Marks,
2418) {
2419    core_ctx.counters().net_unreachable.increment();
2420
2421    // Check whether we MUST NOT send an ICMP error message
2422    // because the original packet was itself an ICMP error message.
2423    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
2424        return;
2425    }
2426
2427    send_icmpv4_dest_unreachable(
2428        core_ctx,
2429        bindings_ctx,
2430        Some(device),
2431        frame_dst,
2432        src_ip,
2433        dst_ip,
2434        Icmpv4DestUnreachableCode::DestNetworkUnreachable,
2435        original_packet,
2436        header_len,
2437        fragment_type,
2438        marks,
2439    );
2440}
2441
2442/// Send an ICMPv6 message in response to receiving a packet destined for an
2443/// unreachable network.
2444///
2445/// `send_icmpv6_net_unreachable` is like [`send_icmpv4_net_unreachable`], but
2446/// for ICMPv6. It sends an ICMPv6 "destination unreachable" message with a "no
2447/// route to destination" code.
2448///
2449/// `original_packet` contains the contents of the entire original packet
2450/// including extension headers. `header_len` is the length of the IP header and
2451/// all extension headers.
2452pub(crate) fn send_icmpv6_net_unreachable<
2453    B: BufferMut,
2454    BC: IcmpBindingsContext,
2455    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2456>(
2457    core_ctx: &mut CC,
2458    bindings_ctx: &mut BC,
2459    device: &CC::DeviceId,
2460    frame_dst: Option<FrameDestination>,
2461    src_ip: SocketIpAddr<Ipv6Addr>,
2462    dst_ip: SocketIpAddr<Ipv6Addr>,
2463    proto: Ipv6Proto,
2464    original_packet: B,
2465    header_len: usize,
2466    marks: &Marks,
2467) {
2468    core_ctx.counters().net_unreachable.increment();
2469
2470    // Check whether we MUST NOT send an ICMP error message
2471    // because the original packet was itself an ICMP error message.
2472    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2473        return;
2474    }
2475
2476    send_icmpv6_dest_unreachable(
2477        core_ctx,
2478        bindings_ctx,
2479        Some(device),
2480        frame_dst,
2481        src_ip,
2482        dst_ip,
2483        Icmpv6DestUnreachableCode::NoRoute,
2484        original_packet,
2485        marks,
2486    );
2487}
2488
2489/// Send an ICMP(v4) message in response to receiving a packet whose TTL has
2490/// expired.
2491///
2492/// `send_icmpv4_ttl_expired` sends the appropriate ICMP in response to
2493/// receiving an IP packet from `src_ip` to `dst_ip` whose TTL has expired. In
2494/// particular, this is an ICMP "time exceeded" message with a "time to live
2495/// exceeded in transit" code.
2496///
2497/// `original_packet` contains the contents of the entire original packet,
2498/// including the header. `header_len` is the length of the IP header including
2499/// options.
2500pub(crate) fn send_icmpv4_ttl_expired<
2501    B: BufferMut,
2502    BC: IcmpBindingsContext,
2503    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2504>(
2505    core_ctx: &mut CC,
2506    bindings_ctx: &mut BC,
2507    device: &CC::DeviceId,
2508    frame_dst: Option<FrameDestination>,
2509    src_ip: SocketIpAddr<Ipv4Addr>,
2510    dst_ip: SocketIpAddr<Ipv4Addr>,
2511    proto: Ipv4Proto,
2512    original_packet: B,
2513    header_len: usize,
2514    fragment_type: Ipv4FragmentType,
2515    marks: &Marks,
2516) {
2517    core_ctx.counters().ttl_expired.increment();
2518
2519    // Check whether we MUST NOT send an ICMP error message because the original
2520    // packet was itself an ICMP error message.
2521    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
2522        return;
2523    }
2524
2525    send_icmpv4_error_message(
2526        core_ctx,
2527        bindings_ctx,
2528        Some(device),
2529        frame_dst,
2530        src_ip,
2531        dst_ip,
2532        Icmpv4ErrorMessage::TimeExceeded {
2533            message: IcmpTimeExceeded::default(),
2534            code: Icmpv4TimeExceededCode::TtlExpired,
2535        },
2536        original_packet,
2537        header_len,
2538        fragment_type,
2539        marks,
2540    )
2541}
2542
2543/// Send an ICMPv6 message in response to receiving a packet whose hop limit has
2544/// expired.
2545///
2546/// `send_icmpv6_ttl_expired` is like [`send_icmpv4_ttl_expired`], but for
2547/// ICMPv6. It sends an ICMPv6 "time exceeded" message with a "hop limit
2548/// exceeded in transit" code.
2549///
2550/// `original_packet` contains the contents of the entire original packet
2551/// including extension headers. `header_len` is the length of the IP header and
2552/// all extension headers.
2553pub(crate) fn send_icmpv6_ttl_expired<
2554    B: BufferMut,
2555    BC: IcmpBindingsContext,
2556    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2557>(
2558    core_ctx: &mut CC,
2559    bindings_ctx: &mut BC,
2560    device: &CC::DeviceId,
2561    frame_dst: Option<FrameDestination>,
2562    src_ip: SocketIpAddr<Ipv6Addr>,
2563    dst_ip: SocketIpAddr<Ipv6Addr>,
2564    proto: Ipv6Proto,
2565    original_packet: B,
2566    header_len: usize,
2567    marks: &Marks,
2568) {
2569    core_ctx.counters().ttl_expired.increment();
2570
2571    // Check whether we MUST NOT send an ICMP error message because the
2572    // original packet was itself an ICMP error message.
2573    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2574        return;
2575    }
2576
2577    send_icmpv6_error_message(
2578        core_ctx,
2579        bindings_ctx,
2580        Some(device),
2581        frame_dst,
2582        src_ip,
2583        dst_ip,
2584        Icmpv6ErrorMessage::TimeExceeded {
2585            message: IcmpTimeExceeded::default(),
2586            code: Icmpv6TimeExceededCode::HopLimitExceeded,
2587        },
2588        original_packet,
2589        false, /* allow_dst_multicast */
2590        marks,
2591    )
2592}
2593
2594// TODO(joshlf): Test send_icmpv6_packet_too_big once we support fake IPv6 test
2595// setups.
2596
2597/// Send an ICMPv6 message in response to receiving a packet whose size exceeds
2598/// the MTU of the next hop interface.
2599///
2600/// `send_icmpv6_packet_too_big` sends an ICMPv6 "packet too big" message in
2601/// response to receiving an IP packet from `src_ip` to `dst_ip` whose size
2602/// exceeds the `mtu` of the next hop interface.
2603pub(crate) fn send_icmpv6_packet_too_big<
2604    B: BufferMut,
2605    BC: IcmpBindingsContext,
2606    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2607>(
2608    core_ctx: &mut CC,
2609    bindings_ctx: &mut BC,
2610    device: &CC::DeviceId,
2611    frame_dst: Option<FrameDestination>,
2612    src_ip: SocketIpAddr<Ipv6Addr>,
2613    dst_ip: SocketIpAddr<Ipv6Addr>,
2614    proto: Ipv6Proto,
2615    mtu: Mtu,
2616    original_packet: B,
2617    header_len: usize,
2618    marks: &Marks,
2619) {
2620    core_ctx.counters().packet_too_big.increment();
2621    // Check whether we MUST NOT send an ICMP error message because the
2622    // original packet was itself an ICMP error message.
2623    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2624        return;
2625    }
2626
2627    send_icmpv6_error_message(
2628        core_ctx,
2629        bindings_ctx,
2630        Some(device),
2631        frame_dst,
2632        src_ip,
2633        dst_ip,
2634        Icmpv6ErrorMessage::PacketTooBig {
2635            message: Icmpv6PacketTooBig::new(mtu.into()),
2636            code: IcmpZeroCode,
2637        },
2638        original_packet,
2639        // As per RFC 4443 section 2.4.e,
2640        //
2641        //   An ICMPv6 error message MUST NOT be originated as a result of
2642        //   receiving the following:
2643        //
2644        //     (e.3) A packet destined to an IPv6 multicast address.  (There are
2645        //           two exceptions to this rule: (1) the Packet Too Big Message
2646        //           (Section 3.2) to allow Path MTU discovery to work for IPv6
2647        //           multicast, and (2) the Parameter Problem Message, Code 2
2648        //           (Section 3.4) reporting an unrecognized IPv6 option (see
2649        //           Section 4.2 of [IPv6]) that has the Option Type highest-
2650        //           order two bits set to 10).
2651        //
2652        //     (e.4) A packet sent as a link-layer multicast (the exceptions
2653        //           from e.3 apply to this case, too).
2654        //
2655        // Thus, we explicitly allow sending a Packet Too Big error if the
2656        // destination was a multicast packet.
2657        true, /* allow_dst_multicast */
2658        marks,
2659    )
2660}
2661
2662pub(crate) fn send_icmpv4_parameter_problem<
2663    B: BufferMut,
2664    BC: IcmpBindingsContext,
2665    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2666>(
2667    core_ctx: &mut CC,
2668    bindings_ctx: &mut BC,
2669    device: &CC::DeviceId,
2670    frame_dst: Option<FrameDestination>,
2671    src_ip: SocketIpAddr<Ipv4Addr>,
2672    dst_ip: SocketIpAddr<Ipv4Addr>,
2673    code: Icmpv4ParameterProblemCode,
2674    parameter_problem: Icmpv4ParameterProblem,
2675    original_packet: B,
2676    header_len: usize,
2677    fragment_type: Ipv4FragmentType,
2678    marks: &Marks,
2679) {
2680    core_ctx.counters().parameter_problem.increment();
2681
2682    send_icmpv4_error_message(
2683        core_ctx,
2684        bindings_ctx,
2685        Some(device),
2686        frame_dst,
2687        src_ip,
2688        dst_ip,
2689        Icmpv4ErrorMessage::ParameterProblem { message: parameter_problem, code },
2690        original_packet,
2691        header_len,
2692        fragment_type,
2693        marks,
2694    )
2695}
2696
2697/// Send an ICMPv6 Parameter Problem error message.
2698///
2699/// If the error message is Code 2 reporting an unrecognized IPv6 option that
2700/// has the Option Type highest-order two bits set to 10, `allow_dst_multicast`
2701/// must be set to `true`. See [`should_send_icmpv6_error`] for more details.
2702///
2703/// # Panics
2704///
2705/// Panics if `allow_multicast_addr` is set to `true`, but this Parameter
2706/// Problem's code is not 2 (Unrecognized IPv6 Option).
2707pub(crate) fn send_icmpv6_parameter_problem<
2708    B: BufferMut,
2709    BC: IcmpBindingsContext,
2710    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2711>(
2712    core_ctx: &mut CC,
2713    bindings_ctx: &mut BC,
2714    device: &CC::DeviceId,
2715    frame_dst: Option<FrameDestination>,
2716    src_ip: SocketIpAddr<Ipv6Addr>,
2717    dst_ip: SocketIpAddr<Ipv6Addr>,
2718    code: Icmpv6ParameterProblemCode,
2719    parameter_problem: Icmpv6ParameterProblem,
2720    original_packet: B,
2721    allow_dst_multicast: bool,
2722    marks: &Marks,
2723) {
2724    // Only allow the `allow_dst_multicast` parameter to be set if the code is
2725    // the unrecognized IPv6 option as that is one of the few exceptions where
2726    // we can send an ICMP packet in response to a packet that was destined for
2727    // a multicast address.
2728    assert!(!allow_dst_multicast || code == Icmpv6ParameterProblemCode::UnrecognizedIpv6Option);
2729
2730    core_ctx.counters().parameter_problem.increment();
2731
2732    send_icmpv6_error_message(
2733        core_ctx,
2734        bindings_ctx,
2735        Some(device),
2736        frame_dst,
2737        src_ip,
2738        dst_ip,
2739        Icmpv6ErrorMessage::ParameterProblem { message: parameter_problem, code },
2740        original_packet,
2741        allow_dst_multicast,
2742        marks,
2743    )
2744}
2745
2746fn send_icmpv4_dest_unreachable<
2747    B: BufferMut,
2748    BC: IcmpBindingsContext,
2749    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2750>(
2751    core_ctx: &mut CC,
2752    bindings_ctx: &mut BC,
2753    device: Option<&CC::DeviceId>,
2754    frame_dst: Option<FrameDestination>,
2755    src_ip: SocketIpAddr<Ipv4Addr>,
2756    dst_ip: SocketIpAddr<Ipv4Addr>,
2757    code: Icmpv4DestUnreachableCode,
2758    original_packet: B,
2759    header_len: usize,
2760    fragment_type: Ipv4FragmentType,
2761    marks: &Marks,
2762) {
2763    core_ctx.counters().dest_unreachable.increment();
2764    send_icmpv4_error_message(
2765        core_ctx,
2766        bindings_ctx,
2767        device,
2768        frame_dst,
2769        src_ip,
2770        dst_ip,
2771        Icmpv4ErrorMessage::DestUnreachable { message: IcmpDestUnreachable::default(), code },
2772        original_packet,
2773        header_len,
2774        fragment_type,
2775        marks,
2776    )
2777}
2778
2779fn send_icmpv6_dest_unreachable<
2780    B: BufferMut,
2781    BC: IcmpBindingsContext,
2782    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2783>(
2784    core_ctx: &mut CC,
2785    bindings_ctx: &mut BC,
2786    device: Option<&CC::DeviceId>,
2787    frame_dst: Option<FrameDestination>,
2788    src_ip: SocketIpAddr<Ipv6Addr>,
2789    dst_ip: SocketIpAddr<Ipv6Addr>,
2790    code: Icmpv6DestUnreachableCode,
2791    original_packet: B,
2792    marks: &Marks,
2793) {
2794    send_icmpv6_error_message(
2795        core_ctx,
2796        bindings_ctx,
2797        device,
2798        frame_dst,
2799        src_ip,
2800        dst_ip,
2801        Icmpv6ErrorMessage::DestUnreachable { message: IcmpDestUnreachable::default(), code },
2802        original_packet,
2803        false, /* allow_dst_multicast */
2804        marks,
2805    )
2806}
2807
2808/// A type to allow implementing the required filtering traits on a concrete
2809/// subset of message types.
2810#[allow(missing_docs)]
2811enum Icmpv4ErrorMessage {
2812    TimeExceeded {
2813        message: IcmpTimeExceeded,
2814        code: <IcmpTimeExceeded as IcmpMessage<Ipv4>>::Code,
2815    },
2816    ParameterProblem {
2817        message: Icmpv4ParameterProblem,
2818        code: <Icmpv4ParameterProblem as IcmpMessage<Ipv4>>::Code,
2819    },
2820    DestUnreachable {
2821        message: IcmpDestUnreachable,
2822        code: <IcmpDestUnreachable as IcmpMessage<Ipv4>>::Code,
2823    },
2824}
2825
2826fn send_icmpv4_error_message<
2827    B: BufferMut,
2828    BC: IcmpBindingsContext,
2829    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2830>(
2831    core_ctx: &mut CC,
2832    bindings_ctx: &mut BC,
2833    device: Option<&CC::DeviceId>,
2834    frame_dst: Option<FrameDestination>,
2835    original_src_ip: SocketIpAddr<Ipv4Addr>,
2836    original_dst_ip: SocketIpAddr<Ipv4Addr>,
2837    message: Icmpv4ErrorMessage,
2838    mut original_packet: B,
2839    header_len: usize,
2840    fragment_type: Ipv4FragmentType,
2841    marks: &Marks,
2842) {
2843    // TODO(https://fxbug.dev/42177876): Come up with rules for when to send ICMP
2844    // error messages.
2845
2846    if !should_send_icmpv4_error(
2847        frame_dst,
2848        original_src_ip.into(),
2849        original_dst_ip.into(),
2850        fragment_type,
2851    ) {
2852        return;
2853    }
2854
2855    // Per RFC 792, body contains entire IPv4 header + 64 bytes of original
2856    // body.
2857    original_packet.shrink_back_to(header_len + 64);
2858
2859    let tx_metadata: BC::TxMetadata = Default::default();
2860
2861    macro_rules! send {
2862        ($message:expr, $code:expr) => {{
2863            // TODO(https://fxbug.dev/42177877): Improve source address selection for ICMP
2864            // errors sent from unnumbered/router interfaces.
2865            let _ = try_send_error!(
2866                core_ctx,
2867                bindings_ctx,
2868                core_ctx.send_oneshot_ip_packet_with_dyn_serializer(
2869                    bindings_ctx,
2870                    IpSocketArgs {
2871                        device: device.map(EitherDeviceId::Strong),
2872                        local_ip: None,
2873                        remote_ip: original_src_ip,
2874                        proto: Ipv4Proto::Icmp,
2875                        options: &WithMarks(marks),
2876                    },
2877                    tx_metadata,
2878                    |local_ip| {
2879                        IcmpPacketBuilder::<Ipv4, _>::new(
2880                            local_ip.addr(),
2881                            original_src_ip.addr(),
2882                            $code,
2883                            $message,
2884                        )
2885                        .wrap_body(original_packet)
2886                    },
2887                )
2888            );
2889        }};
2890    }
2891
2892    match message {
2893        Icmpv4ErrorMessage::TimeExceeded { message, code } => send!(message, code),
2894        Icmpv4ErrorMessage::ParameterProblem { message, code } => send!(message, code),
2895        Icmpv4ErrorMessage::DestUnreachable { message, code } => send!(message, code),
2896    }
2897}
2898
2899/// A type to allow implementing the required filtering traits on a concrete
2900/// subset of message types.
2901#[allow(missing_docs)]
2902enum Icmpv6ErrorMessage {
2903    TimeExceeded {
2904        message: IcmpTimeExceeded,
2905        code: <IcmpTimeExceeded as IcmpMessage<Ipv6>>::Code,
2906    },
2907    PacketTooBig {
2908        message: Icmpv6PacketTooBig,
2909        code: <Icmpv6PacketTooBig as IcmpMessage<Ipv6>>::Code,
2910    },
2911    ParameterProblem {
2912        message: Icmpv6ParameterProblem,
2913        code: <Icmpv6ParameterProblem as IcmpMessage<Ipv6>>::Code,
2914    },
2915    DestUnreachable {
2916        message: IcmpDestUnreachable,
2917        code: <IcmpDestUnreachable as IcmpMessage<Ipv6>>::Code,
2918    },
2919}
2920
2921fn send_icmpv6_error_message<
2922    B: BufferMut,
2923    BC: IcmpBindingsContext,
2924    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2925>(
2926    core_ctx: &mut CC,
2927    bindings_ctx: &mut BC,
2928    device: Option<&CC::DeviceId>,
2929    frame_dst: Option<FrameDestination>,
2930    original_src_ip: SocketIpAddr<Ipv6Addr>,
2931    original_dst_ip: SocketIpAddr<Ipv6Addr>,
2932    message: Icmpv6ErrorMessage,
2933    original_packet: B,
2934    allow_dst_multicast: bool,
2935    marks: &Marks,
2936) {
2937    // TODO(https://fxbug.dev/42177876): Come up with rules for when to send ICMP
2938    // error messages.
2939
2940    if !should_send_icmpv6_error(
2941        frame_dst,
2942        original_src_ip.into(),
2943        original_dst_ip.into(),
2944        allow_dst_multicast,
2945    ) {
2946        return;
2947    }
2948
2949    struct Icmpv6ErrorOptions<'a>(&'a Marks);
2950    impl<'a> OptionDelegationMarker for Icmpv6ErrorOptions<'a> {}
2951    impl<'a> DelegatedSendOptions<Ipv6> for Icmpv6ErrorOptions<'a> {
2952        fn mtu(&self) -> Mtu {
2953            Ipv6::MINIMUM_LINK_MTU
2954        }
2955    }
2956    impl<'a> DelegatedRouteResolutionOptions<Ipv6> for Icmpv6ErrorOptions<'a> {
2957        fn marks(&self) -> &Marks {
2958            let Self(marks) = self;
2959            marks
2960        }
2961    }
2962
2963    let tx_metadata: BC::TxMetadata = Default::default();
2964
2965    macro_rules! send {
2966        ($message:expr, $code:expr) => {{
2967            // TODO(https://fxbug.dev/42177877): Improve source address selection for ICMP
2968            // errors sent from unnumbered/router interfaces.
2969            let _ = try_send_error!(
2970                core_ctx,
2971                bindings_ctx,
2972                core_ctx.send_oneshot_ip_packet_with_dyn_serializer(
2973                    bindings_ctx,
2974                    IpSocketArgs {
2975                        device: device.map(EitherDeviceId::Strong),
2976                        local_ip: None,
2977                        remote_ip: original_src_ip,
2978                        proto: Ipv6Proto::Icmpv6,
2979                        options: &Icmpv6ErrorOptions(marks),
2980                    },
2981                    tx_metadata,
2982                    |local_ip| {
2983                        let icmp_builder = IcmpPacketBuilder::<Ipv6, _>::new(
2984                            local_ip.addr(),
2985                            original_src_ip.addr(),
2986                            $code,
2987                            $message,
2988                        );
2989
2990                        // Per RFC 4443, body contains as much of the original body as
2991                        // possible without exceeding IPv6 minimum MTU.
2992                        icmp_builder.wrap_body(TruncatingSerializer::new(
2993                            original_packet,
2994                            TruncateDirection::DiscardBack,
2995                        ))
2996                    },
2997                )
2998            );
2999        }};
3000    }
3001
3002    match message {
3003        Icmpv6ErrorMessage::TimeExceeded { message, code } => send!(message, code),
3004        Icmpv6ErrorMessage::PacketTooBig { message, code } => send!(message, code),
3005        Icmpv6ErrorMessage::ParameterProblem { message, code } => send!(message, code),
3006        Icmpv6ErrorMessage::DestUnreachable { message, code } => send!(message, code),
3007    }
3008}
3009
3010/// Should we send an ICMP(v4) response?
3011///
3012/// `should_send_icmpv4_error` implements the logic described in RFC 1122
3013/// Section 3.2.2. It decides whether, upon receiving an incoming packet with
3014/// the given parameters, we should send an ICMP response or not. In particular,
3015/// we do not send an ICMP response if we've received:
3016/// - a packet destined to a broadcast or multicast address
3017/// - a packet sent in a link-layer broadcast
3018/// - a non-initial fragment
3019/// - a packet whose source address does not define a single host (a
3020///   zero/unspecified address, a loopback address, a broadcast address, a
3021///   multicast address, or a Class E address)
3022///
3023/// Note that `should_send_icmpv4_error` does NOT check whether the incoming
3024/// packet contained an ICMP error message. This is because that check is
3025/// unnecessary for some ICMP error conditions. The ICMP error message check can
3026/// be performed separately with `is_icmp_error_message`.
3027fn should_send_icmpv4_error(
3028    frame_dst: Option<FrameDestination>,
3029    src_ip: SpecifiedAddr<Ipv4Addr>,
3030    dst_ip: SpecifiedAddr<Ipv4Addr>,
3031    fragment_type: Ipv4FragmentType,
3032) -> bool {
3033    // NOTE: We do not explicitly implement the "unspecified address" check, as
3034    // it is enforced by the types of the arguments.
3035
3036    // TODO(joshlf): Implement the rest of the rules:
3037    // - a packet destined to a subnet broadcast address
3038    // - a packet whose source address is a subnet broadcast address
3039
3040    // NOTE: The FrameDestination type has variants for unicast, multicast, and
3041    // broadcast. One implication of the fact that we only check for broadcast
3042    // here (in compliance with the RFC) is that we could, in one very unlikely
3043    // edge case, respond with an ICMP error message to an IP packet which was
3044    // sent in a link-layer multicast frame. In particular, that can happen if
3045    // we subscribe to a multicast IP group and, as a result, subscribe to the
3046    // corresponding multicast MAC address, and we receive a unicast IP packet
3047    // in a multicast link-layer frame destined to that MAC address.
3048    //
3049    // TODO(joshlf): Should we filter incoming multicast IP traffic to make sure
3050    // that it matches the multicast MAC address of the frame it was
3051    // encapsulated in?
3052    fragment_type == Ipv4FragmentType::InitialFragment
3053        && !(dst_ip.is_multicast()
3054            || dst_ip.is_limited_broadcast()
3055            || frame_dst.is_some_and(|dst| dst.is_broadcast())
3056            || src_ip.is_loopback()
3057            || src_ip.is_limited_broadcast()
3058            || src_ip.is_multicast()
3059            || src_ip.is_class_e())
3060}
3061
3062/// Should we send an ICMPv6 response?
3063///
3064/// `should_send_icmpv6_error` implements the logic described in RFC 4443
3065/// Section 2.4.e. It decides whether, upon receiving an incoming packet with
3066/// the given parameters, we should send an ICMP response or not. In particular,
3067/// we do not send an ICMP response if we've received:
3068/// - a packet destined to a multicast address
3069///   - Two exceptions to this rules:
3070///     1) the Packet Too Big Message to allow Path MTU discovery to work for
3071///        IPv6 multicast
3072///     2) the Parameter Problem Message, Code 2 reporting an unrecognized IPv6
3073///        option that has the Option Type highest-order two bits set to 10
3074/// - a packet sent as a link-layer multicast or broadcast
3075///   - same exceptions apply here as well.
3076/// - a packet whose source address does not define a single host (a
3077///   zero/unspecified address, a loopback address, or a multicast address)
3078///
3079/// If an ICMP response will be a Packet Too Big Message or a Parameter Problem
3080/// Message, Code 2 reporting an unrecognized IPv6 option that has the Option
3081/// Type highest-order two bits set to 10, `info.allow_dst_multicast` must be
3082/// set to `true` so this function will allow the exception mentioned above.
3083///
3084/// Note that `should_send_icmpv6_error` does NOT check whether the incoming
3085/// packet contained an ICMP error message. This is because that check is
3086/// unnecessary for some ICMP error conditions. The ICMP error message check can
3087/// be performed separately with `is_icmp_error_message`.
3088fn should_send_icmpv6_error(
3089    frame_dst: Option<FrameDestination>,
3090    src_ip: SpecifiedAddr<Ipv6Addr>,
3091    dst_ip: SpecifiedAddr<Ipv6Addr>,
3092    allow_dst_multicast: bool,
3093) -> bool {
3094    // NOTE: We do not explicitly implement the "unspecified address" check, as
3095    // it is enforced by the types of the arguments.
3096    let multicast_frame_dst = match frame_dst {
3097        Some(FrameDestination::Individual { local: _ }) | None => false,
3098        Some(FrameDestination::Broadcast) | Some(FrameDestination::Multicast) => true,
3099    };
3100    if (dst_ip.is_multicast() || multicast_frame_dst) && !allow_dst_multicast {
3101        return false;
3102    }
3103    if src_ip.is_loopback() || src_ip.is_multicast() {
3104        return false;
3105    }
3106    true
3107}
3108
3109/// Determine whether or not an IP packet body contains an ICMP error message
3110/// for the purposes of determining whether or not to send an ICMP response.
3111///
3112/// `is_icmp_error_message` checks whether `proto` is ICMP(v4) for IPv4 or
3113/// ICMPv6 for IPv6 and, if so, attempts to parse `buf` as an ICMP packet in
3114/// order to determine whether it is an error message or not. If parsing fails,
3115/// it conservatively assumes that it is an error packet in order to avoid
3116/// violating the MUST NOT directives of RFC 1122 Section 3.2.2 and [RFC 4443
3117/// Section 2.4.e].
3118///
3119/// [RFC 4443 Section 2.4.e]: https://tools.ietf.org/html/rfc4443#section-2.4
3120fn is_icmp_error_message<I: IcmpIpExt>(proto: I::Proto, buf: &[u8]) -> bool {
3121    proto == I::ICMP_IP_PROTO
3122        && peek_message_type::<I::IcmpMessageType>(buf).map(IcmpMessageType::is_err).unwrap_or(true)
3123}
3124
3125/// Test utilities for ICMP.
3126#[cfg(any(test, feature = "testutils"))]
3127pub(crate) mod testutil {
3128    use alloc::vec::Vec;
3129    use net_types::ethernet::Mac;
3130    use net_types::ip::{Ipv6, Ipv6Addr};
3131    use packet::{Buf, InnerPacketBuilder as _, Serializer as _};
3132    use packet_formats::icmp::ndp::options::NdpOptionBuilder;
3133    use packet_formats::icmp::ndp::{
3134        NeighborAdvertisement, NeighborSolicitation, OptionSequenceBuilder,
3135    };
3136    use packet_formats::icmp::{IcmpPacketBuilder, IcmpZeroCode};
3137    use packet_formats::ip::Ipv6Proto;
3138    use packet_formats::ipv6::Ipv6PacketBuilder;
3139
3140    use super::REQUIRED_NDP_IP_PACKET_HOP_LIMIT;
3141
3142    /// Serialize an IP packet containing a neighbor advertisement with the
3143    /// provided parameters.
3144    pub fn neighbor_advertisement_ip_packet(
3145        src_ip: Ipv6Addr,
3146        dst_ip: Ipv6Addr,
3147        router_flag: bool,
3148        solicited_flag: bool,
3149        override_flag: bool,
3150        mac: Mac,
3151    ) -> Buf<Vec<u8>> {
3152        OptionSequenceBuilder::new([NdpOptionBuilder::TargetLinkLayerAddress(&mac.bytes())].iter())
3153            .into_serializer()
3154            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
3155                src_ip,
3156                dst_ip,
3157                IcmpZeroCode,
3158                NeighborAdvertisement::new(router_flag, solicited_flag, override_flag, src_ip),
3159            ))
3160            .wrap_in(Ipv6PacketBuilder::new(
3161                src_ip,
3162                dst_ip,
3163                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
3164                Ipv6Proto::Icmpv6,
3165            ))
3166            .serialize_vec_outer()
3167            .unwrap()
3168            .unwrap_b()
3169    }
3170
3171    /// Serialize an IP packet containing a neighbor solicitation with the
3172    /// provided parameters.
3173    pub fn neighbor_solicitation_ip_packet(
3174        src_ip: Ipv6Addr,
3175        dst_ip: Ipv6Addr,
3176        target_addr: Ipv6Addr,
3177        mac: Mac,
3178    ) -> Buf<Vec<u8>> {
3179        OptionSequenceBuilder::new([NdpOptionBuilder::SourceLinkLayerAddress(&mac.bytes())].iter())
3180            .into_serializer()
3181            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
3182                src_ip,
3183                dst_ip,
3184                IcmpZeroCode,
3185                NeighborSolicitation::new(target_addr),
3186            ))
3187            .wrap_in(Ipv6PacketBuilder::new(
3188                src_ip,
3189                dst_ip,
3190                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
3191                Ipv6Proto::Icmpv6,
3192            ))
3193            .serialize_vec_outer()
3194            .unwrap()
3195            .unwrap_b()
3196    }
3197}
3198
3199#[cfg(test)]
3200mod tests {
3201    use alloc::vec;
3202    use alloc::vec::Vec;
3203    use packet_formats::icmp::ndp::options::NdpNonce;
3204
3205    use core::fmt::Debug;
3206    use core::time::Duration;
3207
3208    use net_types::ip::Subnet;
3209    use netstack3_base::testutil::{
3210        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeInstant, FakeTxMetadata, FakeWeakDeviceId,
3211        TEST_ADDRS_V4, TEST_ADDRS_V6, TestIpExt, set_logger_for_test,
3212    };
3213    use netstack3_base::{CtxPair, Uninstantiable};
3214    use netstack3_filter::TransportPacketSerializer;
3215    use packet::{Buf, EmptyBuf};
3216    use packet_formats::icmp::mld::MldPacket;
3217    use packet_formats::ip::IpProto;
3218    use packet_formats::utils::NonZeroDuration;
3219
3220    use super::*;
3221    use crate::internal::base::{IpDeviceEgressStateContext, RouterAdvertisementEvent};
3222    use crate::internal::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx};
3223    use crate::internal::socket::{
3224        IpSock, IpSockCreationError, IpSockSendError, IpSocketHandler, SendOptions,
3225    };
3226    use crate::socket::RouteResolutionOptions;
3227
3228    /// The FakeCoreCtx held as the inner state of [`FakeIcmpCoreCtx`].
3229    type InnerIpSocketCtx<I> = FakeCoreCtx<
3230        FakeIpSocketCtx<I, FakeDeviceId>,
3231        SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
3232        FakeDeviceId,
3233    >;
3234
3235    /// `FakeCoreCtx` specialized for ICMP.
3236    pub(super) struct FakeIcmpCoreCtx<I: IpExt> {
3237        ip_socket_ctx: InnerIpSocketCtx<I>,
3238        icmp: FakeIcmpCoreCtxState<I>,
3239    }
3240
3241    /// `FakeBindingsCtx` specialized for ICMP.
3242    type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<
3243        (),
3244        RouterAdvertisementEvent<FakeDeviceId>,
3245        FakeIcmpBindingsCtxState<I>,
3246        (),
3247    >;
3248
3249    /// A fake ICMP bindings and core contexts.
3250    ///
3251    /// This is exposed to super so it can be shared with the socket tests.
3252    pub(super) type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
3253
3254    pub(super) struct FakeIcmpCoreCtxState<I: IpExt> {
3255        error_send_bucket: TokenBucket<FakeInstant>,
3256        receive_icmp_error: Vec<I::ErrorCode>,
3257        rx_counters: IcmpRxCounters<I>,
3258        tx_counters: IcmpTxCounters<I>,
3259        ndp_counters: NdpCounters,
3260    }
3261
3262    impl<I: TestIpExt + IpExt> FakeIcmpCoreCtx<I> {
3263        fn with_errors_per_second(errors_per_second: u64) -> Self {
3264            Self {
3265                icmp: FakeIcmpCoreCtxState {
3266                    error_send_bucket: TokenBucket::new(errors_per_second),
3267                    receive_icmp_error: Default::default(),
3268                    rx_counters: Default::default(),
3269                    tx_counters: Default::default(),
3270                    ndp_counters: Default::default(),
3271                },
3272                ip_socket_ctx: InnerIpSocketCtx::with_state(FakeIpSocketCtx::new(
3273                    core::iter::once(FakeDeviceConfig {
3274                        device: FakeDeviceId,
3275                        local_ips: vec![I::TEST_ADDRS.local_ip],
3276                        remote_ips: vec![I::TEST_ADDRS.remote_ip],
3277                    }),
3278                )),
3279            }
3280        }
3281    }
3282
3283    impl<I: TestIpExt + IpExt> Default for FakeIcmpCoreCtx<I> {
3284        fn default() -> Self {
3285            Self::with_errors_per_second(DEFAULT_ERRORS_PER_SECOND)
3286        }
3287    }
3288
3289    impl<I: IpExt> DeviceIdContext<AnyDevice> for FakeIcmpCoreCtx<I> {
3290        type DeviceId = FakeDeviceId;
3291        type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
3292    }
3293
3294    impl<I: IpExt> IcmpStateContext for FakeIcmpCoreCtx<I> {}
3295    impl<I: IpExt> IcmpStateContext for InnerIpSocketCtx<I> {}
3296
3297    impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtx<I> {
3298        fn counters(&self) -> &IcmpRxCounters<I> {
3299            &self.icmp.rx_counters
3300        }
3301    }
3302
3303    impl<I: IpExt> CounterContext<IcmpTxCounters<I>> for FakeIcmpCoreCtx<I> {
3304        fn counters(&self) -> &IcmpTxCounters<I> {
3305            &self.icmp.tx_counters
3306        }
3307    }
3308
3309    impl<I: IpExt> CounterContext<NdpCounters> for FakeIcmpCoreCtx<I> {
3310        fn counters(&self) -> &NdpCounters {
3311            &self.icmp.ndp_counters
3312        }
3313    }
3314
3315    pub enum FakeEchoIpTransportContext {}
3316
3317    impl EchoTransportContextMarker for FakeEchoIpTransportContext {}
3318
3319    impl<I: IpExt> IpTransportContext<I, FakeIcmpBindingsCtx<I>, FakeIcmpCoreCtx<I>>
3320        for FakeEchoIpTransportContext
3321    {
3322        fn receive_icmp_error(
3323            core_ctx: &mut FakeIcmpCoreCtx<I>,
3324            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3325            _device: &FakeDeviceId,
3326            _original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3327            _original_dst_ip: SpecifiedAddr<I::Addr>,
3328            _original_body: &[u8],
3329            _err: I::ErrorCode,
3330        ) {
3331            core_ctx.icmp.rx_counters.error_delivered_to_socket.increment()
3332        }
3333
3334        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
3335            _core_ctx: &mut FakeIcmpCoreCtx<I>,
3336            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3337            _device: &FakeDeviceId,
3338            _src_ip: I::RecvSrcAddr,
3339            _dst_ip: SpecifiedAddr<I::Addr>,
3340            _buffer: B,
3341            _info: &LocalDeliveryPacketInfo<I, H>,
3342        ) -> Result<(), (B, TransportReceiveError)> {
3343            unimplemented!()
3344        }
3345    }
3346
3347    impl<I: IpExt + FilterIpExt> InnerIcmpContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3348        type EchoTransportContext = FakeEchoIpTransportContext;
3349
3350        fn receive_icmp_error(
3351            &mut self,
3352            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3353            device: &Self::DeviceId,
3354            original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3355            original_dst_ip: SpecifiedAddr<I::Addr>,
3356            original_proto: I::Proto,
3357            original_body: &[u8],
3358            err: I::ErrorCode,
3359        ) {
3360            CounterContext::<IcmpRxCounters<I>>::counters(self).error.increment();
3361            self.icmp.receive_icmp_error.push(err);
3362            if original_proto == I::ICMP_IP_PROTO {
3363                receive_ip_transport_icmp_error(
3364                    self,
3365                    bindings_ctx,
3366                    device,
3367                    original_src_ip,
3368                    original_dst_ip,
3369                    original_body,
3370                    err,
3371                )
3372            }
3373        }
3374
3375        fn with_error_send_bucket_mut<O, F: FnOnce(&mut TokenBucket<FakeInstant>) -> O>(
3376            &mut self,
3377            cb: F,
3378        ) -> O {
3379            cb(&mut self.icmp.error_send_bucket)
3380        }
3381    }
3382
3383    #[test]
3384    fn test_should_send_icmpv4_error() {
3385        let src_ip = TEST_ADDRS_V4.local_ip;
3386        let dst_ip = TEST_ADDRS_V4.remote_ip;
3387        let frame_dst = FrameDestination::Individual { local: true };
3388        let multicast_ip_1 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 1])).unwrap();
3389        let multicast_ip_2 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 2])).unwrap();
3390
3391        // Should Send, unless non initial fragment.
3392        assert!(should_send_icmpv4_error(
3393            Some(frame_dst),
3394            src_ip,
3395            dst_ip,
3396            Ipv4FragmentType::InitialFragment
3397        ));
3398        assert!(should_send_icmpv4_error(None, src_ip, dst_ip, Ipv4FragmentType::InitialFragment));
3399        assert!(!should_send_icmpv4_error(
3400            Some(frame_dst),
3401            src_ip,
3402            dst_ip,
3403            Ipv4FragmentType::NonInitialFragment
3404        ));
3405
3406        // Should not send because destined for IP broadcast addr
3407        assert!(!should_send_icmpv4_error(
3408            Some(frame_dst),
3409            src_ip,
3410            Ipv4::LIMITED_BROADCAST_ADDRESS,
3411            Ipv4FragmentType::InitialFragment
3412        ));
3413        assert!(!should_send_icmpv4_error(
3414            Some(frame_dst),
3415            src_ip,
3416            Ipv4::LIMITED_BROADCAST_ADDRESS,
3417            Ipv4FragmentType::NonInitialFragment
3418        ));
3419
3420        // Should not send because destined for multicast addr
3421        assert!(!should_send_icmpv4_error(
3422            Some(frame_dst),
3423            src_ip,
3424            multicast_ip_1,
3425            Ipv4FragmentType::InitialFragment
3426        ));
3427        assert!(!should_send_icmpv4_error(
3428            Some(frame_dst),
3429            src_ip,
3430            multicast_ip_1,
3431            Ipv4FragmentType::NonInitialFragment
3432        ));
3433
3434        // Should not send because Link Layer Broadcast.
3435        assert!(!should_send_icmpv4_error(
3436            Some(FrameDestination::Broadcast),
3437            src_ip,
3438            dst_ip,
3439            Ipv4FragmentType::InitialFragment
3440        ));
3441        assert!(!should_send_icmpv4_error(
3442            Some(FrameDestination::Broadcast),
3443            src_ip,
3444            dst_ip,
3445            Ipv4FragmentType::NonInitialFragment
3446        ));
3447
3448        // Should not send because from loopback addr
3449        assert!(!should_send_icmpv4_error(
3450            Some(frame_dst),
3451            Ipv4::LOOPBACK_ADDRESS,
3452            dst_ip,
3453            Ipv4FragmentType::InitialFragment
3454        ));
3455        assert!(!should_send_icmpv4_error(
3456            Some(frame_dst),
3457            Ipv4::LOOPBACK_ADDRESS,
3458            dst_ip,
3459            Ipv4FragmentType::NonInitialFragment
3460        ));
3461
3462        // Should not send because from limited broadcast addr
3463        assert!(!should_send_icmpv4_error(
3464            Some(frame_dst),
3465            Ipv4::LIMITED_BROADCAST_ADDRESS,
3466            dst_ip,
3467            Ipv4FragmentType::InitialFragment
3468        ));
3469        assert!(!should_send_icmpv4_error(
3470            Some(frame_dst),
3471            Ipv4::LIMITED_BROADCAST_ADDRESS,
3472            dst_ip,
3473            Ipv4FragmentType::NonInitialFragment
3474        ));
3475
3476        // Should not send because from multicast addr
3477        assert!(!should_send_icmpv4_error(
3478            Some(frame_dst),
3479            multicast_ip_2,
3480            dst_ip,
3481            Ipv4FragmentType::InitialFragment
3482        ));
3483        assert!(!should_send_icmpv4_error(
3484            Some(frame_dst),
3485            multicast_ip_2,
3486            dst_ip,
3487            Ipv4FragmentType::NonInitialFragment
3488        ));
3489
3490        // Should not send because from class E addr
3491        assert!(!should_send_icmpv4_error(
3492            Some(frame_dst),
3493            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
3494            dst_ip,
3495            Ipv4FragmentType::InitialFragment
3496        ));
3497        assert!(!should_send_icmpv4_error(
3498            Some(frame_dst),
3499            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
3500            dst_ip,
3501            Ipv4FragmentType::NonInitialFragment
3502        ));
3503    }
3504
3505    #[test]
3506    fn test_should_send_icmpv6_error() {
3507        let src_ip = TEST_ADDRS_V6.local_ip;
3508        let dst_ip = TEST_ADDRS_V6.remote_ip;
3509        let frame_dst = FrameDestination::Individual { local: true };
3510        let multicast_ip_1 =
3511            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 1])).unwrap();
3512        let multicast_ip_2 =
3513            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 2])).unwrap();
3514
3515        // Should Send.
3516        assert!(should_send_icmpv6_error(
3517            Some(frame_dst),
3518            src_ip,
3519            dst_ip,
3520            false /* allow_dst_multicast */
3521        ));
3522        assert!(should_send_icmpv6_error(
3523            None, src_ip, dst_ip, false /* allow_dst_multicast */
3524        ));
3525        assert!(should_send_icmpv6_error(
3526            Some(frame_dst),
3527            src_ip,
3528            dst_ip,
3529            true /* allow_dst_multicast */
3530        ));
3531
3532        // Should not send because destined for multicast addr, unless exception
3533        // applies.
3534        assert!(!should_send_icmpv6_error(
3535            Some(frame_dst),
3536            src_ip,
3537            multicast_ip_1,
3538            false /* allow_dst_multicast */
3539        ));
3540        assert!(should_send_icmpv6_error(
3541            Some(frame_dst),
3542            src_ip,
3543            multicast_ip_1,
3544            true /* allow_dst_multicast */
3545        ));
3546
3547        // Should not send because Link Layer Broadcast, unless exception
3548        // applies.
3549        assert!(!should_send_icmpv6_error(
3550            Some(FrameDestination::Broadcast),
3551            src_ip,
3552            dst_ip,
3553            false /* allow_dst_multicast */
3554        ));
3555        assert!(should_send_icmpv6_error(
3556            Some(FrameDestination::Broadcast),
3557            src_ip,
3558            dst_ip,
3559            true /* allow_dst_multicast */
3560        ));
3561
3562        // Should not send because from loopback addr.
3563        assert!(!should_send_icmpv6_error(
3564            Some(frame_dst),
3565            Ipv6::LOOPBACK_ADDRESS,
3566            dst_ip,
3567            false /* allow_dst_multicast */
3568        ));
3569        assert!(!should_send_icmpv6_error(
3570            Some(frame_dst),
3571            Ipv6::LOOPBACK_ADDRESS,
3572            dst_ip,
3573            true /* allow_dst_multicast */
3574        ));
3575
3576        // Should not send because from multicast addr.
3577        assert!(!should_send_icmpv6_error(
3578            Some(frame_dst),
3579            multicast_ip_2,
3580            dst_ip,
3581            false /* allow_dst_multicast */
3582        ));
3583        assert!(!should_send_icmpv6_error(
3584            Some(frame_dst),
3585            multicast_ip_2,
3586            dst_ip,
3587            true /* allow_dst_multicast */
3588        ));
3589
3590        // Should not send because from multicast addr, even though dest
3591        // multicast exception applies.
3592        assert!(!should_send_icmpv6_error(
3593            Some(FrameDestination::Broadcast),
3594            multicast_ip_2,
3595            dst_ip,
3596            false /* allow_dst_multicast */
3597        ));
3598        assert!(!should_send_icmpv6_error(
3599            Some(FrameDestination::Broadcast),
3600            multicast_ip_2,
3601            dst_ip,
3602            true /* allow_dst_multicast */
3603        ));
3604        assert!(!should_send_icmpv6_error(
3605            Some(frame_dst),
3606            multicast_ip_2,
3607            multicast_ip_1,
3608            false /* allow_dst_multicast */
3609        ));
3610        assert!(!should_send_icmpv6_error(
3611            Some(frame_dst),
3612            multicast_ip_2,
3613            multicast_ip_1,
3614            true /* allow_dst_multicast */
3615        ));
3616    }
3617
3618    // Tests that only require an ICMP stack. Unlike the preceding tests, these
3619    // only test the ICMP stack and state, and fake everything else. We define
3620    // the `FakeIcmpv4Ctx` and `FakeIcmpv6Ctx` types, which we wrap in a
3621    // `FakeCtx` to provide automatic implementations of a number of required
3622    // traits. The rest we implement manually.
3623
3624    #[derive(Default)]
3625    pub(super) struct FakeIcmpBindingsCtxState<I: IpExt> {
3626        _marker: core::marker::PhantomData<I>,
3627    }
3628
3629    impl InnerIcmpv4Context<FakeIcmpBindingsCtx<Ipv4>> for FakeIcmpCoreCtx<Ipv4> {
3630        fn should_send_timestamp_reply(&self) -> bool {
3631            false
3632        }
3633    }
3634    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv4>, FakeIcmpBindingsCtx<Ipv4>, Ipv4);
3635    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv6>, FakeIcmpBindingsCtx<Ipv6>, Ipv6);
3636
3637    impl<I: IpExt + FilterIpExt> IpSocketHandler<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3638        fn new_ip_socket<O>(
3639            &mut self,
3640            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3641            args: IpSocketArgs<'_, Self::DeviceId, I, O>,
3642        ) -> Result<IpSock<I, Self::WeakDeviceId>, IpSockCreationError>
3643        where
3644            O: RouteResolutionOptions<I>,
3645        {
3646            self.ip_socket_ctx.new_ip_socket(bindings_ctx, args)
3647        }
3648
3649        fn send_ip_packet<S, O>(
3650            &mut self,
3651            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3652            socket: &IpSock<I, Self::WeakDeviceId>,
3653            body: S,
3654            options: &O,
3655            tx_meta: FakeTxMetadata,
3656        ) -> Result<(), IpSockSendError>
3657        where
3658            S: TransportPacketSerializer<I>,
3659            S::Buffer: BufferMut,
3660            O: SendOptions<I> + RouteResolutionOptions<I>,
3661        {
3662            self.ip_socket_ctx.send_ip_packet(bindings_ctx, socket, body, options, tx_meta)
3663        }
3664
3665        fn confirm_reachable<O>(
3666            &mut self,
3667            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3668            socket: &IpSock<I, Self::WeakDeviceId>,
3669            options: &O,
3670        ) where
3671            O: RouteResolutionOptions<I>,
3672        {
3673            self.ip_socket_ctx.confirm_reachable(bindings_ctx, socket, options)
3674        }
3675    }
3676
3677    impl IpDeviceHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3678        fn is_router_device(&mut self, _device_id: &Self::DeviceId) -> bool {
3679            unimplemented!()
3680        }
3681
3682        fn set_default_hop_limit(&mut self, _device_id: &Self::DeviceId, _hop_limit: NonZeroU8) {
3683            unreachable!()
3684        }
3685
3686        fn handle_received_dad_packet(
3687            &mut self,
3688            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3689            _device_id: &Self::DeviceId,
3690            _addr: SpecifiedAddr<Ipv6Addr>,
3691            _probe_data: Option<NdpNonce<&'_ [u8]>>,
3692        ) -> Option<IpAddressState> {
3693            unimplemented!()
3694        }
3695    }
3696
3697    impl IpDeviceEgressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3698        fn with_next_packet_id<O, F: FnOnce(&()) -> O>(&self, cb: F) -> O {
3699            cb(&())
3700        }
3701
3702        fn get_local_addr_for_remote(
3703            &mut self,
3704            _device_id: &Self::DeviceId,
3705            _remote: Option<SpecifiedAddr<Ipv6Addr>>,
3706        ) -> Option<IpDeviceAddr<Ipv6Addr>> {
3707            unimplemented!()
3708        }
3709
3710        fn get_hop_limit(&mut self, _device_id: &Self::DeviceId) -> NonZeroU8 {
3711            unimplemented!()
3712        }
3713    }
3714
3715    impl IpDeviceIngressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3716        fn address_status_for_device(
3717            &mut self,
3718            _addr: SpecifiedAddr<Ipv6Addr>,
3719            _device_id: &Self::DeviceId,
3720        ) -> AddressStatus<Ipv6PresentAddressStatus> {
3721            unimplemented!()
3722        }
3723    }
3724
3725    impl Ipv6DeviceHandler<FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3726        type LinkLayerAddr = Uninstantiable;
3727
3728        fn get_link_layer_addr(&mut self, _device_id: &Self::DeviceId) -> Option<Uninstantiable> {
3729            unimplemented!()
3730        }
3731
3732        fn set_discovered_retrans_timer(
3733            &mut self,
3734            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3735            _device_id: &Self::DeviceId,
3736            _retrans_timer: NonZeroDuration,
3737        ) {
3738            unimplemented!()
3739        }
3740
3741        fn set_link_mtu(&mut self, _device_id: &Self::DeviceId, _mtu: Mtu) {
3742            unimplemented!()
3743        }
3744
3745        fn update_discovered_ipv6_route(
3746            &mut self,
3747            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3748            _device_id: &Self::DeviceId,
3749            _route: Ipv6DiscoveredRoute,
3750            _lifetime: Option<NonZeroNdpLifetime>,
3751        ) {
3752            unimplemented!()
3753        }
3754
3755        fn apply_slaac_update(
3756            &mut self,
3757            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3758            _device_id: &Self::DeviceId,
3759            _subnet: Subnet<Ipv6Addr>,
3760            _preferred_lifetime: Option<NonZeroNdpLifetime>,
3761            _valid_lifetime: Option<NonZeroNdpLifetime>,
3762        ) {
3763            unimplemented!()
3764        }
3765
3766        fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
3767            &mut self,
3768            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3769            _device: &FakeDeviceId,
3770            _src_ip: Ipv6SourceAddr,
3771            _dst_ip: SpecifiedAddr<Ipv6Addr>,
3772            _packet: MldPacket<B>,
3773            _header_info: &H,
3774        ) {
3775            unimplemented!()
3776        }
3777    }
3778
3779    impl IpLayerHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3780        fn send_ip_packet_from_device<S>(
3781            &mut self,
3782            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3783            _meta: SendIpPacketMeta<Ipv6, &Self::DeviceId, Option<SpecifiedAddr<Ipv6Addr>>>,
3784            _body: S,
3785        ) -> Result<(), IpSendFrameError<S>> {
3786            unimplemented!()
3787        }
3788
3789        fn send_ip_frame<S>(
3790            &mut self,
3791            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3792            _device: &Self::DeviceId,
3793            _destination: IpPacketDestination<Ipv6, &Self::DeviceId>,
3794            _body: S,
3795        ) -> Result<(), IpSendFrameError<S>>
3796        where
3797            S: Serializer,
3798            S::Buffer: BufferMut,
3799        {
3800            unimplemented!()
3801        }
3802    }
3803
3804    impl NudIpHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3805        fn handle_neighbor_probe(
3806            &mut self,
3807            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3808            _device_id: &Self::DeviceId,
3809            _neighbor: SpecifiedAddr<Ipv6Addr>,
3810            _link_addr: &[u8],
3811        ) {
3812            unimplemented!()
3813        }
3814
3815        fn handle_neighbor_confirmation(
3816            &mut self,
3817            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3818            _device_id: &Self::DeviceId,
3819            _neighbor: SpecifiedAddr<Ipv6Addr>,
3820            _link_addr: Option<&[u8]>,
3821            _flags: ConfirmationFlags,
3822        ) {
3823            unimplemented!()
3824        }
3825
3826        fn flush_neighbor_table(
3827            &mut self,
3828            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3829            _device_id: &Self::DeviceId,
3830        ) {
3831            unimplemented!()
3832        }
3833    }
3834
3835    #[test]
3836    fn test_receive_icmpv4_error() {
3837        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
3838        // the value 0) and, b) different from each other.
3839        const ICMP_ID: u16 = 0x0F;
3840        const SEQ_NUM: u16 = 0xF0;
3841
3842        /// Test receiving an ICMP error message.
3843        ///
3844        /// Test that receiving an ICMP error message with the given code and
3845        /// message contents, and containing the given original IPv4 packet,
3846        /// results in the counter values in `assert_counters`. After that
3847        /// assertion passes, `f` is called on the context so that the caller
3848        /// can perform whatever extra validation they want.
3849        ///
3850        /// The error message will be sent from `TEST_ADDRS_V4.remote_ip` to
3851        /// `TEST_ADDRS_V4.local_ip`. Before the message is sent, an ICMP
3852        /// socket will be established with the ID `ICMP_ID`, and
3853        /// `test_receive_icmpv4_error_helper` will assert that its `SocketId`
3854        /// is 0. This allows the caller to craft the `original_packet` so that
3855        /// it should be delivered to this socket.
3856        fn test_receive_icmpv4_error_helper<
3857            C: Debug,
3858            M: IcmpMessage<Ipv4, Code = C> + Debug,
3859            F: Fn(&FakeIcmpCtx<Ipv4>),
3860        >(
3861            original_packet: &mut [u8],
3862            code: C,
3863            msg: M,
3864            f: F,
3865        ) {
3866            set_logger_for_test();
3867
3868            let mut ctx: FakeIcmpCtx<Ipv4> = FakeIcmpCtx::default();
3869
3870            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
3871            <IcmpIpTransportContext as IpTransportContext<Ipv4, _, _>>::receive_ip_packet(
3872                core_ctx,
3873                bindings_ctx,
3874                &FakeDeviceId,
3875                Ipv4SourceAddr::new(*TEST_ADDRS_V4.remote_ip).unwrap(),
3876                TEST_ADDRS_V4.local_ip,
3877                IcmpPacketBuilder::new(TEST_ADDRS_V4.remote_ip, TEST_ADDRS_V4.local_ip, code, msg)
3878                    .wrap_body(Buf::new(original_packet, ..))
3879                    .serialize_vec_outer()
3880                    .unwrap(),
3881                &LocalDeliveryPacketInfo::default(),
3882            )
3883            .unwrap();
3884            f(&ctx);
3885        }
3886        // Test that, when we receive various ICMPv4 error messages, we properly
3887        // pass them up to the IP layer and, sometimes, to the transport layer.
3888
3889        // First, test with an original packet containing an ICMP message. Since
3890        // this test fake supports ICMP sockets, this error can be delivered all
3891        // the way up the stack.
3892
3893        // A buffer containing an ICMP echo request with ID `ICMP_ID` and
3894        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
3895        // ICMP error message which contains this as its original packet should
3896        // be delivered to the socket created in
3897        // `test_receive_icmpv4_error_helper`.
3898        let mut buffer = EmptyBuf
3899            .wrap_in(IcmpPacketBuilder::<Ipv4, _>::new(
3900                TEST_ADDRS_V4.local_ip,
3901                TEST_ADDRS_V4.remote_ip,
3902                IcmpZeroCode,
3903                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
3904            ))
3905            .wrap_in(<Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3906                TEST_ADDRS_V4.local_ip,
3907                TEST_ADDRS_V4.remote_ip,
3908                64,
3909                Ipv4Proto::Icmp,
3910            ))
3911            .serialize_vec_outer()
3912            .unwrap();
3913
3914        test_receive_icmpv4_error_helper(
3915            buffer.as_mut(),
3916            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3917            IcmpDestUnreachable::default(),
3918            |CtxPair { core_ctx, bindings_ctx: _ }| {
3919                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3920                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3921                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3922                let err = Icmpv4ErrorCode::DestUnreachable(
3923                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3924                    IcmpDestUnreachable::default(),
3925                );
3926                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3927            },
3928        );
3929
3930        test_receive_icmpv4_error_helper(
3931            buffer.as_mut(),
3932            Icmpv4TimeExceededCode::TtlExpired,
3933            IcmpTimeExceeded::default(),
3934            |CtxPair { core_ctx, bindings_ctx: _ }| {
3935                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3936                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3937                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3938                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3939                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3940            },
3941        );
3942
3943        test_receive_icmpv4_error_helper(
3944            buffer.as_mut(),
3945            Icmpv4ParameterProblemCode::PointerIndicatesError,
3946            Icmpv4ParameterProblem::new(0),
3947            |CtxPair { core_ctx, bindings_ctx: _ }| {
3948                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3949                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3950                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3951                let err = Icmpv4ErrorCode::ParameterProblem(
3952                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3953                );
3954                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3955            },
3956        );
3957
3958        // Second, test with an original packet containing a malformed ICMP
3959        // packet (we accomplish this by leaving the IP packet's body empty). We
3960        // should process this packet in
3961        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
3962        // further - in particular, we should not dispatch to the Echo sockets.
3963
3964        let mut buffer = <Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3965            TEST_ADDRS_V4.local_ip,
3966            TEST_ADDRS_V4.remote_ip,
3967            64,
3968            Ipv4Proto::Icmp,
3969        )
3970        .wrap_body(EmptyBuf)
3971        .serialize_vec_outer()
3972        .unwrap();
3973
3974        test_receive_icmpv4_error_helper(
3975            buffer.as_mut(),
3976            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3977            IcmpDestUnreachable::default(),
3978            |CtxPair { core_ctx, bindings_ctx: _ }| {
3979                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3980                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3981                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3982                let err = Icmpv4ErrorCode::DestUnreachable(
3983                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3984                    IcmpDestUnreachable::default(),
3985                );
3986                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3987            },
3988        );
3989
3990        test_receive_icmpv4_error_helper(
3991            buffer.as_mut(),
3992            Icmpv4TimeExceededCode::TtlExpired,
3993            IcmpTimeExceeded::default(),
3994            |CtxPair { core_ctx, bindings_ctx: _ }| {
3995                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3996                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3997                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3998                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3999                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4000            },
4001        );
4002
4003        test_receive_icmpv4_error_helper(
4004            buffer.as_mut(),
4005            Icmpv4ParameterProblemCode::PointerIndicatesError,
4006            Icmpv4ParameterProblem::new(0),
4007            |CtxPair { core_ctx, bindings_ctx: _ }| {
4008                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4009                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4010                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4011                let err = Icmpv4ErrorCode::ParameterProblem(
4012                    Icmpv4ParameterProblemCode::PointerIndicatesError,
4013                );
4014                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4015            },
4016        );
4017
4018        // Third, test with an original packet containing a UDP packet. This
4019        // allows us to verify that protocol numbers are handled properly by
4020        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
4021        // called.
4022
4023        let mut buffer = <Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
4024            TEST_ADDRS_V4.local_ip,
4025            TEST_ADDRS_V4.remote_ip,
4026            64,
4027            IpProto::Udp.into(),
4028        )
4029        .wrap_body(EmptyBuf)
4030        .serialize_vec_outer()
4031        .unwrap();
4032
4033        test_receive_icmpv4_error_helper(
4034            buffer.as_mut(),
4035            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
4036            IcmpDestUnreachable::default(),
4037            |CtxPair { core_ctx, bindings_ctx: _ }| {
4038                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4039                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4040                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4041                let err = Icmpv4ErrorCode::DestUnreachable(
4042                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
4043                    IcmpDestUnreachable::default(),
4044                );
4045                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4046            },
4047        );
4048
4049        test_receive_icmpv4_error_helper(
4050            buffer.as_mut(),
4051            Icmpv4TimeExceededCode::TtlExpired,
4052            IcmpTimeExceeded::default(),
4053            |CtxPair { core_ctx, bindings_ctx: _ }| {
4054                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4055                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4056                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4057                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
4058                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4059            },
4060        );
4061
4062        test_receive_icmpv4_error_helper(
4063            buffer.as_mut(),
4064            Icmpv4ParameterProblemCode::PointerIndicatesError,
4065            Icmpv4ParameterProblem::new(0),
4066            |CtxPair { core_ctx, bindings_ctx: _ }| {
4067                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4068                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4069                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4070                let err = Icmpv4ErrorCode::ParameterProblem(
4071                    Icmpv4ParameterProblemCode::PointerIndicatesError,
4072                );
4073                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4074            },
4075        );
4076    }
4077
4078    #[test]
4079    fn test_receive_icmpv6_error() {
4080        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
4081        // the value 0) and, b) different from each other.
4082        const ICMP_ID: u16 = 0x0F;
4083        const SEQ_NUM: u16 = 0xF0;
4084
4085        /// Test receiving an ICMPv6 error message.
4086        ///
4087        /// Test that receiving an ICMP error message with the given code and
4088        /// message contents, and containing the given original IPv4 packet,
4089        /// results in the counter values in `assert_counters`. After that
4090        /// assertion passes, `f` is called on the context so that the caller
4091        /// can perform whatever extra validation they want.
4092        ///
4093        /// The error message will be sent from `TEST_ADDRS_V6.remote_ip` to
4094        /// `TEST_ADDRS_V6.local_ip`. Before the message is sent, an ICMP
4095        /// socket will be established with the ID `ICMP_ID`, and
4096        /// `test_receive_icmpv6_error_helper` will assert that its `SocketId`
4097        /// is 0. This allows the caller to craft the `original_packet` so that
4098        /// it should be delivered to this socket.
4099        fn test_receive_icmpv6_error_helper<
4100            C: Debug,
4101            M: IcmpMessage<Ipv6, Code = C> + Debug,
4102            F: Fn(&FakeIcmpCtx<Ipv6>),
4103        >(
4104            original_packet: &mut [u8],
4105            code: C,
4106            msg: M,
4107            f: F,
4108        ) {
4109            set_logger_for_test();
4110
4111            let mut ctx = FakeIcmpCtx::<Ipv6>::default();
4112            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
4113            <IcmpIpTransportContext as IpTransportContext<Ipv6, _, _>>::receive_ip_packet(
4114                core_ctx,
4115                bindings_ctx,
4116                &FakeDeviceId,
4117                TEST_ADDRS_V6.remote_ip.get().try_into().unwrap(),
4118                TEST_ADDRS_V6.local_ip,
4119                IcmpPacketBuilder::new(TEST_ADDRS_V6.remote_ip, TEST_ADDRS_V6.local_ip, code, msg)
4120                    .wrap_body(Buf::new(original_packet, ..))
4121                    .serialize_vec_outer()
4122                    .unwrap(),
4123                &LocalDeliveryPacketInfo::default(),
4124            )
4125            .unwrap();
4126            f(&ctx);
4127        }
4128        // Test that, when we receive various ICMPv6 error messages, we properly
4129        // pass them up to the IP layer and, sometimes, to the transport layer.
4130
4131        // First, test with an original packet containing an ICMPv6 message.
4132        // Since this test fake supports ICMPv6 sockets, this error can be
4133        // delivered all the way up the stack.
4134
4135        // A buffer containing an ICMPv6 echo request with ID `ICMP_ID` and
4136        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
4137        // ICMPv6 error message which contains this as its original packet
4138        // should be delivered to the socket created in
4139        // `test_receive_icmpv6_error_helper`.
4140        let mut buffer = EmptyBuf
4141            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
4142                TEST_ADDRS_V6.local_ip,
4143                TEST_ADDRS_V6.remote_ip,
4144                IcmpZeroCode,
4145                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
4146            ))
4147            .wrap_in(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4148                TEST_ADDRS_V6.local_ip,
4149                TEST_ADDRS_V6.remote_ip,
4150                64,
4151                Ipv6Proto::Icmpv6,
4152            ))
4153            .serialize_vec_outer()
4154            .unwrap();
4155
4156        test_receive_icmpv6_error_helper(
4157            buffer.as_mut(),
4158            Icmpv6DestUnreachableCode::NoRoute,
4159            IcmpDestUnreachable::default(),
4160            |CtxPair { core_ctx, bindings_ctx: _ }| {
4161                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4162                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4163                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4164                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4165                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4166            },
4167        );
4168
4169        test_receive_icmpv6_error_helper(
4170            buffer.as_mut(),
4171            Icmpv6TimeExceededCode::HopLimitExceeded,
4172            IcmpTimeExceeded::default(),
4173            |CtxPair { core_ctx, bindings_ctx: _ }| {
4174                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4175                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4176                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4177                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4178                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4179            },
4180        );
4181
4182        test_receive_icmpv6_error_helper(
4183            buffer.as_mut(),
4184            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4185            Icmpv6ParameterProblem::new(0),
4186            |CtxPair { core_ctx, bindings_ctx: _ }| {
4187                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4188                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4189                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4190                let err = Icmpv6ErrorCode::ParameterProblem(
4191                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4192                );
4193                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4194            },
4195        );
4196
4197        // Second, test with an original packet containing a malformed ICMPv6
4198        // packet (we accomplish this by leaving the IP packet's body empty). We
4199        // should process this packet in
4200        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
4201        // further - in particular, we should not call into Echo sockets.
4202
4203        let mut buffer = EmptyBuf
4204            .wrap_in(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4205                TEST_ADDRS_V6.local_ip,
4206                TEST_ADDRS_V6.remote_ip,
4207                64,
4208                Ipv6Proto::Icmpv6,
4209            ))
4210            .serialize_vec_outer()
4211            .unwrap();
4212
4213        test_receive_icmpv6_error_helper(
4214            buffer.as_mut(),
4215            Icmpv6DestUnreachableCode::NoRoute,
4216            IcmpDestUnreachable::default(),
4217            |CtxPair { core_ctx, bindings_ctx: _ }| {
4218                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4219                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4220                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4221                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4222                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4223            },
4224        );
4225
4226        test_receive_icmpv6_error_helper(
4227            buffer.as_mut(),
4228            Icmpv6TimeExceededCode::HopLimitExceeded,
4229            IcmpTimeExceeded::default(),
4230            |CtxPair { core_ctx, bindings_ctx: _ }| {
4231                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4232                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4233                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4234                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4235                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4236            },
4237        );
4238
4239        test_receive_icmpv6_error_helper(
4240            buffer.as_mut(),
4241            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4242            Icmpv6ParameterProblem::new(0),
4243            |CtxPair { core_ctx, bindings_ctx: _ }| {
4244                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4245                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4246                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4247                let err = Icmpv6ErrorCode::ParameterProblem(
4248                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4249                );
4250                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4251            },
4252        );
4253
4254        // Third, test with an original packet containing a UDP packet. This
4255        // allows us to verify that protocol numbers are handled properly by
4256        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
4257        // called.
4258
4259        let mut buffer = <Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4260            TEST_ADDRS_V6.local_ip,
4261            TEST_ADDRS_V6.remote_ip,
4262            64,
4263            IpProto::Udp.into(),
4264        )
4265        .wrap_body(EmptyBuf)
4266        .serialize_vec_outer()
4267        .unwrap();
4268
4269        test_receive_icmpv6_error_helper(
4270            buffer.as_mut(),
4271            Icmpv6DestUnreachableCode::NoRoute,
4272            IcmpDestUnreachable::default(),
4273            |CtxPair { core_ctx, bindings_ctx: _ }| {
4274                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4275                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4276                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4277                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4278                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4279            },
4280        );
4281
4282        test_receive_icmpv6_error_helper(
4283            buffer.as_mut(),
4284            Icmpv6TimeExceededCode::HopLimitExceeded,
4285            IcmpTimeExceeded::default(),
4286            |CtxPair { core_ctx, bindings_ctx: _ }| {
4287                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4288                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4289                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4290                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4291                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4292            },
4293        );
4294
4295        test_receive_icmpv6_error_helper(
4296            buffer.as_mut(),
4297            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4298            Icmpv6ParameterProblem::new(0),
4299            |CtxPair { core_ctx, bindings_ctx: _ }| {
4300                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4301                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4302                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4303                let err = Icmpv6ErrorCode::ParameterProblem(
4304                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4305                );
4306                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4307            },
4308        );
4309    }
4310
4311    #[test]
4312    fn test_error_rate_limit() {
4313        set_logger_for_test();
4314
4315        /// Call `send_icmpv4_ttl_expired` with fake values.
4316        fn send_icmpv4_ttl_expired_helper(
4317            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4318        ) {
4319            send_icmpv4_ttl_expired(
4320                core_ctx,
4321                bindings_ctx,
4322                &FakeDeviceId,
4323                Some(FrameDestination::Individual { local: true }),
4324                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4325                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4326                IpProto::Udp.into(),
4327                EmptyBuf,
4328                0,
4329                Ipv4FragmentType::InitialFragment,
4330                &Default::default(),
4331            );
4332        }
4333
4334        /// Call `send_icmpv4_parameter_problem` with fake values.
4335        fn send_icmpv4_parameter_problem_helper(
4336            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4337        ) {
4338            send_icmpv4_parameter_problem(
4339                core_ctx,
4340                bindings_ctx,
4341                &FakeDeviceId,
4342                Some(FrameDestination::Individual { local: true }),
4343                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4344                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4345                Icmpv4ParameterProblemCode::PointerIndicatesError,
4346                Icmpv4ParameterProblem::new(0),
4347                EmptyBuf,
4348                0,
4349                Ipv4FragmentType::InitialFragment,
4350                &Default::default(),
4351            );
4352        }
4353
4354        /// Call `send_icmpv4_dest_unreachable` with fake values.
4355        fn send_icmpv4_dest_unreachable_helper(
4356            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4357        ) {
4358            send_icmpv4_dest_unreachable(
4359                core_ctx,
4360                bindings_ctx,
4361                Some(&FakeDeviceId),
4362                Some(FrameDestination::Individual { local: true }),
4363                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4364                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4365                Icmpv4DestUnreachableCode::DestNetworkUnreachable,
4366                EmptyBuf,
4367                0,
4368                Ipv4FragmentType::InitialFragment,
4369                &Default::default(),
4370            );
4371        }
4372
4373        /// Call `send_icmpv6_ttl_expired` with fake values.
4374        fn send_icmpv6_ttl_expired_helper(
4375            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4376        ) {
4377            send_icmpv6_ttl_expired(
4378                core_ctx,
4379                bindings_ctx,
4380                &FakeDeviceId,
4381                Some(FrameDestination::Individual { local: true }),
4382                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4383                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4384                IpProto::Udp.into(),
4385                EmptyBuf,
4386                0,
4387                &Default::default(),
4388            );
4389        }
4390
4391        /// Call `send_icmpv6_packet_too_big` with fake values.
4392        fn send_icmpv6_packet_too_big_helper(
4393            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4394        ) {
4395            send_icmpv6_packet_too_big(
4396                core_ctx,
4397                bindings_ctx,
4398                &FakeDeviceId,
4399                Some(FrameDestination::Individual { local: true }),
4400                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4401                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4402                IpProto::Udp.into(),
4403                Mtu::new(0),
4404                EmptyBuf,
4405                0,
4406                &Default::default(),
4407            );
4408        }
4409
4410        /// Call `send_icmpv6_parameter_problem` with fake values.
4411        fn send_icmpv6_parameter_problem_helper(
4412            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4413        ) {
4414            send_icmpv6_parameter_problem(
4415                core_ctx,
4416                bindings_ctx,
4417                &FakeDeviceId,
4418                Some(FrameDestination::Individual { local: true }),
4419                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4420                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4421                Icmpv6ParameterProblemCode::ErroneousHeaderField,
4422                Icmpv6ParameterProblem::new(0),
4423                EmptyBuf,
4424                false,
4425                &Default::default(),
4426            );
4427        }
4428
4429        /// Call `send_icmpv6_dest_unreachable` with fake values.
4430        fn send_icmpv6_dest_unreachable_helper(
4431            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4432        ) {
4433            send_icmpv6_dest_unreachable(
4434                core_ctx,
4435                bindings_ctx,
4436                Some(&FakeDeviceId),
4437                Some(FrameDestination::Individual { local: true }),
4438                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4439                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4440                Icmpv6DestUnreachableCode::NoRoute,
4441                EmptyBuf,
4442                &Default::default(),
4443            );
4444        }
4445
4446        // Run tests for each function that sends error messages to make sure
4447        // they're all properly rate limited.
4448
4449        fn run_test<I: IpExt, W: Fn(u64) -> FakeIcmpCtx<I>, S: Fn(&mut FakeIcmpCtx<I>)>(
4450            with_errors_per_second: W,
4451            send: S,
4452        ) {
4453            // Note that we could theoretically have more precise tests here
4454            // (e.g., a test that we send at the correct rate over the long
4455            // term), but those would amount to testing the `TokenBucket`
4456            // implementation, which has its own exhaustive tests. Instead, we
4457            // just have a few sanity checks to make sure that we're actually
4458            // invoking it when we expect to (as opposed to bypassing it
4459            // entirely or something).
4460
4461            // Test that, if no time has elapsed, we can successfully send up to
4462            // `ERRORS_PER_SECOND` error messages, but no more.
4463
4464            // Don't use `DEFAULT_ERRORS_PER_SECOND` because it's 2^16 and it
4465            // makes this test take a long time.
4466            const ERRORS_PER_SECOND: u64 = 64;
4467
4468            let mut ctx = with_errors_per_second(ERRORS_PER_SECOND);
4469
4470            for i in 0..ERRORS_PER_SECOND {
4471                send(&mut ctx);
4472                assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), i + 1);
4473            }
4474
4475            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
4476            send(&mut ctx);
4477            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
4478
4479            // Test that, if we set a rate of 0, we are not able to send any
4480            // error messages regardless of how much time has elapsed.
4481
4482            let mut ctx = with_errors_per_second(0);
4483            send(&mut ctx);
4484            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4485            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4486            send(&mut ctx);
4487            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4488            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4489            send(&mut ctx);
4490            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4491        }
4492
4493        fn with_errors_per_second_v4(errors_per_second: u64) -> FakeIcmpCtx<Ipv4> {
4494            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4495        }
4496        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_ttl_expired_helper);
4497        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_parameter_problem_helper);
4498        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_dest_unreachable_helper);
4499
4500        fn with_errors_per_second_v6(errors_per_second: u64) -> FakeIcmpCtx<Ipv6> {
4501            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4502        }
4503
4504        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_ttl_expired_helper);
4505        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_packet_too_big_helper);
4506        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_parameter_problem_helper);
4507        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_dest_unreachable_helper);
4508    }
4509}