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        false,
1253        device_addr.get(),
1254    );
1255    let _: Result<(), _> = send_ndp_packet(
1256        core_ctx,
1257        bindings_ctx,
1258        &device_id,
1259        Some(device_addr.into_specified()),
1260        dst_ip,
1261        OptionSequenceBuilder::new(
1262            src_ll
1263                .as_ref()
1264                .map(Ipv6LinkLayerAddr::as_bytes)
1265                .map(NdpOptionBuilder::TargetLinkLayerAddress)
1266                .iter(),
1267        )
1268        .into_serializer(),
1269        NdpMessage::NeighborAdvertisement { message: advertisement, code: IcmpZeroCode },
1270    );
1271}
1272
1273fn receive_ndp_packet<
1274    B: SplitByteSlice,
1275    BC: IcmpBindingsContext + NdpBindingsContext<CC::DeviceId>,
1276    CC: InnerIcmpv6Context<BC>
1277        + Ipv6DeviceHandler<BC>
1278        + IpDeviceHandler<Ipv6, BC>
1279        + IpDeviceIngressStateContext<Ipv6>
1280        + NudIpHandler<Ipv6, BC>
1281        + IpLayerHandler<Ipv6, BC>
1282        + CounterContext<NdpCounters>,
1283    H: IpHeaderInfo<Ipv6>,
1284>(
1285    core_ctx: &mut CC,
1286    bindings_ctx: &mut BC,
1287    device_id: &CC::DeviceId,
1288    src_ip: Ipv6SourceAddr,
1289    packet: NdpPacket<B>,
1290    header_info: &H,
1291) {
1292    // All NDP messages should be dropped if the hop-limit != 255. See
1293    //   Router Solicitations: RFC 4861 section 6.1.1,
1294    //   Router Advertisements: RFC 4861 section 6.1.2,
1295    //   Neighbor Solicitations: RFC 4861 section 7.1.1,
1296    //   Neighbor Advertisements: RFC 4861 section 7.1.2, and
1297    //   Redirect: RFC 4861 section 8.1:
1298    //
1299    //       A node MUST silently discard any received [NDP Message Type]
1300    //       messages that do not satisfy all of the following validity
1301    //       checks:
1302    //
1303    //          ...
1304    //
1305    //          - The IP Hop Limit field has a value of 255, i.e., the packet
1306    //            could not possibly have been forwarded by a router.
1307    //
1308    //          ...
1309    if header_info.hop_limit() != REQUIRED_NDP_IP_PACKET_HOP_LIMIT {
1310        trace!("dropping NDP packet from {src_ip} with invalid hop limit");
1311        return;
1312    }
1313
1314    match packet {
1315        NdpPacket::RouterSolicitation(_) | NdpPacket::Redirect(_) => {}
1316        NdpPacket::NeighborSolicitation(ref p) => {
1317            let target_address = p.message().target_address();
1318            let target_address = match UnicastAddr::new(*target_address) {
1319                Some(a) => a,
1320                None => {
1321                    trace!(
1322                        "dropping NS from {} with non-unicast target={:?}",
1323                        src_ip, target_address
1324                    );
1325                    return;
1326                }
1327            };
1328
1329            core_ctx.counters().rx.neighbor_solicitation.increment();
1330
1331            match src_ip {
1332                Ipv6SourceAddr::Unspecified => {
1333                    // The neighbor is performing Duplicate address detection.
1334                    //
1335                    // As per RFC 4861 section 4.3,
1336                    //
1337                    //   Source Address
1338                    //       Either an address assigned to the interface from
1339                    //       which this message is sent or (if Duplicate Address
1340                    //       Detection is in progress [ADDRCONF]) the
1341                    //       unspecified address.
1342                    match IpDeviceHandler::handle_received_dad_packet(
1343                        core_ctx,
1344                        bindings_ctx,
1345                        &device_id,
1346                        target_address.into_specified(),
1347                        p.body().iter().find_map(|option| option.nonce()),
1348                    ) {
1349                        IpAddressState::Assigned => {
1350                            // Address is assigned to us to we let the
1351                            // remote node performing DAD that we own the
1352                            // address.
1353                            send_neighbor_advertisement(
1354                                core_ctx,
1355                                bindings_ctx,
1356                                &device_id,
1357                                false,
1358                                target_address,
1359                                Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS.into_specified(),
1360                            );
1361                        }
1362                        IpAddressState::Tentative => {
1363                            // Nothing further to do in response to DAD
1364                            // messages.
1365                        }
1366                        IpAddressState::Unavailable => {
1367                            // Nothing further to do for unassigned target
1368                            // addresses.
1369                        }
1370                    }
1371
1372                    return;
1373                }
1374                Ipv6SourceAddr::Unicast(src_ip) => {
1375                    // Neighbor is performing link address resolution.
1376                    match core_ctx
1377                        .address_status_for_device(target_address.into_specified(), device_id)
1378                    {
1379                        AddressStatus::Present(Ipv6PresentAddressStatus::UnicastAssigned) => {}
1380                        AddressStatus::Present(
1381                            Ipv6PresentAddressStatus::UnicastTentative
1382                            | Ipv6PresentAddressStatus::Multicast,
1383                        )
1384                        | AddressStatus::Unassigned => {
1385                            // Address is not considered assigned to us as a
1386                            // unicast so don't send a neighbor advertisement
1387                            // reply.
1388                            return;
1389                        }
1390                    }
1391
1392                    let link_addr = p.body().iter().find_map(|o| o.source_link_layer_address());
1393
1394                    if let Some(link_addr) = link_addr {
1395                        NudIpHandler::handle_neighbor_probe(
1396                            core_ctx,
1397                            bindings_ctx,
1398                            &device_id,
1399                            src_ip.into_specified(),
1400                            link_addr,
1401                        );
1402                    }
1403
1404                    send_neighbor_advertisement(
1405                        core_ctx,
1406                        bindings_ctx,
1407                        &device_id,
1408                        true,
1409                        target_address,
1410                        src_ip.into_specified(),
1411                    );
1412                }
1413            }
1414        }
1415        NdpPacket::NeighborAdvertisement(ref p) => {
1416            // TODO(https://fxbug.dev/42179526): Invalidate discovered routers when
1417            // neighbor entry's IsRouter field transitions to false.
1418
1419            let target_address = p.message().target_address();
1420
1421            let src_ip = match src_ip {
1422                Ipv6SourceAddr::Unicast(src_ip) => src_ip,
1423                Ipv6SourceAddr::Unspecified => {
1424                    trace!("dropping NA with unspecified source and target = {:?}", target_address);
1425                    return;
1426                }
1427            };
1428
1429            let target_address = match UnicastAddr::new(*target_address) {
1430                Some(a) => a,
1431                None => {
1432                    trace!(
1433                        "dropping NA from {} with non-unicast target={:?}",
1434                        src_ip, target_address
1435                    );
1436                    return;
1437                }
1438            };
1439
1440            core_ctx.counters().rx.neighbor_advertisement.increment();
1441
1442            // Note: Neighbor Advertisements don't carry a nonce value. Handle
1443            // a NA in the same way that we would handle an NS that omitted the
1444            // nonce (i.e. conclude it's not-looped-back).
1445            let nonce = None;
1446            match IpDeviceHandler::handle_received_dad_packet(
1447                core_ctx,
1448                bindings_ctx,
1449                &device_id,
1450                target_address.into_specified(),
1451                nonce,
1452            ) {
1453                IpAddressState::Assigned => {
1454                    // A neighbor is advertising that it owns an address
1455                    // that we also have assigned. This is out of scope
1456                    // for DAD.
1457                    //
1458                    // As per RFC 4862 section 5.4.4,
1459                    //
1460                    //   2.  If the target address matches a unicast address
1461                    //       assigned to the receiving interface, it would
1462                    //       possibly indicate that the address is a
1463                    //       duplicate but it has not been detected by the
1464                    //       Duplicate Address Detection procedure (recall
1465                    //       that Duplicate Address Detection is not
1466                    //       completely reliable). How to handle such a case
1467                    //       is beyond the scope of this document.
1468                    //
1469                    // TODO(https://fxbug.dev/42111744): Signal to bindings
1470                    // that a duplicate address is detected.
1471                    error!(
1472                        "NA from {src_ip} with target address {target_address} that is also \
1473                        assigned on device {device_id:?}",
1474                    );
1475                }
1476                IpAddressState::Tentative => {
1477                    // Nothing further to do for an NA from a neighbor that
1478                    // targets an address we also have assigned.
1479                    return;
1480                }
1481                IpAddressState::Unavailable => {
1482                    // Address not targeting us so we know its for a neighbor.
1483                    //
1484                    // TODO(https://fxbug.dev/42182317): Move NUD to IP.
1485                }
1486            }
1487
1488            let link_addr = p.body().iter().find_map(|o| o.target_link_layer_address());
1489
1490            NudIpHandler::handle_neighbor_confirmation(
1491                core_ctx,
1492                bindings_ctx,
1493                &device_id,
1494                target_address.into_specified(),
1495                link_addr,
1496                ConfirmationFlags {
1497                    solicited_flag: p.message().solicited_flag(),
1498                    override_flag: p.message().override_flag(),
1499                },
1500            );
1501        }
1502        NdpPacket::RouterAdvertisement(ref p) => {
1503            // As per RFC 4861 section 6.1.2,
1504            //
1505            //   A node MUST silently discard any received Router Advertisement
1506            //   messages that do not satisfy all of the following validity
1507            //   checks:
1508            //
1509            //      - IP Source Address is a link-local address.  Routers must
1510            //        use their link-local address as the source for Router
1511            //        Advertisement and Redirect messages so that hosts can
1512            //        uniquely identify routers.
1513            //
1514            //        ...
1515            let src_ip = match src_ip {
1516                Ipv6SourceAddr::Unicast(ip) => match LinkLocalUnicastAddr::new(*ip) {
1517                    Some(ip) => ip,
1518                    None => return,
1519                },
1520                Ipv6SourceAddr::Unspecified => return,
1521            };
1522
1523            let ra = p.message();
1524            debug!("received router advertisement from {:?}: {:?}", src_ip, ra);
1525            core_ctx.counters().rx.router_advertisement.increment();
1526
1527            // As per RFC 4861 section 6.3.4,
1528            //   The RetransTimer variable SHOULD be copied from the Retrans
1529            //   Timer field, if it is specified.
1530            //
1531            // TODO(https://fxbug.dev/42052173): Control whether or not we should
1532            // update the retransmit timer.
1533            if let Some(retransmit_timer) = ra.retransmit_timer() {
1534                Ipv6DeviceHandler::set_discovered_retrans_timer(
1535                    core_ctx,
1536                    bindings_ctx,
1537                    &device_id,
1538                    retransmit_timer,
1539                );
1540            }
1541
1542            // As per RFC 4861 section 6.3.4:
1543            //   If the received Cur Hop Limit value is specified, the host
1544            //   SHOULD set its CurHopLimit variable to the received value.
1545            //
1546            // TODO(https://fxbug.dev/42052173): Control whether or not we should
1547            // update the default hop limit.
1548            if let Some(hop_limit) = ra.current_hop_limit() {
1549                trace!(
1550                    "receive_ndp_packet: NDP RA: updating device's hop limit to {:?} for router: {:?}",
1551                    ra.current_hop_limit(),
1552                    src_ip
1553                );
1554                IpDeviceHandler::set_default_hop_limit(core_ctx, &device_id, hop_limit);
1555            }
1556
1557            // TODO(https://fxbug.dev/42077316): Support default router preference.
1558            Ipv6DeviceHandler::update_discovered_ipv6_route(
1559                core_ctx,
1560                bindings_ctx,
1561                &device_id,
1562                Ipv6DiscoveredRoute { subnet: IPV6_DEFAULT_SUBNET, gateway: Some(src_ip) },
1563                p.message().router_lifetime().map(NonZeroNdpLifetime::Finite),
1564            );
1565
1566            for option in p.body().iter() {
1567                match option {
1568                    NdpOption::TargetLinkLayerAddress(_)
1569                    | NdpOption::RedirectedHeader { .. }
1570                    | NdpOption::RecursiveDnsServer(_)
1571                    | NdpOption::Nonce(_) => {}
1572                    NdpOption::SourceLinkLayerAddress(addr) => {
1573                        debug!("processing SourceLinkLayerAddress option in RA: {:?}", addr);
1574                        // As per RFC 4861 section 6.3.4,
1575                        //
1576                        //   If the advertisement contains a Source Link-Layer
1577                        //   Address option, the link-layer address SHOULD be
1578                        //   recorded in the Neighbor Cache entry for the router
1579                        //   (creating an entry if necessary) and the IsRouter
1580                        //   flag in the Neighbor Cache entry MUST be set to
1581                        //   TRUE. If no Source Link-Layer Address is included,
1582                        //   but a corresponding Neighbor Cache entry exists,
1583                        //   its IsRouter flag MUST be set to TRUE. The IsRouter
1584                        //   flag is used by Neighbor Unreachability Detection
1585                        //   to determine when a router changes to being a host
1586                        //   (i.e., no longer capable of forwarding packets).
1587                        //   If a Neighbor Cache entry is created for the
1588                        //   router, its reachability state MUST be set to STALE
1589                        //   as specified in Section 7.3.3.  If a cache entry
1590                        //   already exists and is updated with a different
1591                        //   link-layer address, the reachability state MUST
1592                        //   also be set to STALE.if a Neighbor Cache entry
1593                        //
1594                        // We do not yet support NUD as described in RFC 4861
1595                        // so for now we just record the link-layer address in
1596                        // our neighbor table.
1597                        //
1598                        // TODO(https://fxbug.dev/42083367): Add support for routers in NUD.
1599                        NudIpHandler::handle_neighbor_probe(
1600                            core_ctx,
1601                            bindings_ctx,
1602                            &device_id,
1603                            {
1604                                let src_ip: UnicastAddr<_> = src_ip.into_addr();
1605                                src_ip.into_specified()
1606                            },
1607                            addr,
1608                        );
1609                    }
1610                    NdpOption::PrefixInformation(prefix_info) => {
1611                        debug!("processing Prefix Information option in RA: {:?}", prefix_info);
1612                        // As per RFC 4861 section 6.3.4,
1613                        //
1614                        //   For each Prefix Information option with the on-link
1615                        //   flag set, a host does the following:
1616                        //
1617                        //      - If the prefix is the link-local prefix,
1618                        //        silently ignore the Prefix Information option.
1619                        //
1620                        // Also as per RFC 4862 section 5.5.3,
1621                        //
1622                        //   For each Prefix-Information option in the Router
1623                        //   Advertisement:
1624                        //
1625                        //    ..
1626                        //
1627                        //    b)  If the prefix is the link-local prefix,
1628                        //        silently ignore the Prefix Information option.
1629                        if prefix_info.prefix().is_link_local() {
1630                            continue;
1631                        }
1632
1633                        let subnet = match prefix_info.subnet() {
1634                            Ok(subnet) => subnet,
1635                            Err(err) => match err {
1636                                SubnetError::PrefixTooLong | SubnetError::HostBitsSet => continue,
1637                            },
1638                        };
1639
1640                        match UnicastAddr::new(subnet.network()) {
1641                            Some(UnicastAddr { .. }) => {}
1642                            None => continue,
1643                        }
1644
1645                        let valid_lifetime = prefix_info.valid_lifetime();
1646
1647                        if prefix_info.on_link_flag() {
1648                            // TODO(https://fxbug.dev/42077316): Support route preference.
1649                            Ipv6DeviceHandler::update_discovered_ipv6_route(
1650                                core_ctx,
1651                                bindings_ctx,
1652                                &device_id,
1653                                Ipv6DiscoveredRoute { subnet, gateway: None },
1654                                valid_lifetime,
1655                            )
1656                        }
1657
1658                        if prefix_info.autonomous_address_configuration_flag() {
1659                            Ipv6DeviceHandler::apply_slaac_update(
1660                                core_ctx,
1661                                bindings_ctx,
1662                                &device_id,
1663                                subnet,
1664                                prefix_info.preferred_lifetime(),
1665                                valid_lifetime,
1666                            );
1667                        }
1668                    }
1669                    NdpOption::RouteInformation(rio) => {
1670                        debug!("processing Route Information option in RA: {:?}", rio);
1671                        // TODO(https://fxbug.dev/42077316): Support route preference.
1672                        Ipv6DeviceHandler::update_discovered_ipv6_route(
1673                            core_ctx,
1674                            bindings_ctx,
1675                            &device_id,
1676                            Ipv6DiscoveredRoute {
1677                                subnet: rio.prefix().clone(),
1678                                gateway: Some(src_ip),
1679                            },
1680                            rio.route_lifetime(),
1681                        )
1682                    }
1683                    NdpOption::Mtu(mtu) => {
1684                        debug!("processing MTU option in RA: {:?}", mtu);
1685                        // TODO(https://fxbug.dev/42052173): Control whether or
1686                        // not we should update the link's MTU in response to
1687                        // RAs.
1688                        Ipv6DeviceHandler::set_link_mtu(core_ctx, &device_id, Mtu::new(mtu));
1689                    }
1690                }
1691            }
1692
1693            bindings_ctx.on_event(RouterAdvertisementEvent {
1694                options_bytes: Box::from(p.body().bytes()),
1695                source: **src_ip,
1696                device: device_id.clone(),
1697            });
1698        }
1699    }
1700}
1701
1702impl<
1703    BC: IcmpBindingsContext + NdpBindingsContext<CC::DeviceId>,
1704    CC: InnerIcmpv6Context<BC>
1705        + InnerIcmpContext<Ipv6, BC>
1706        + Ipv6DeviceHandler<BC>
1707        + IpDeviceHandler<Ipv6, BC>
1708        + IpDeviceIngressStateContext<Ipv6>
1709        + PmtuHandler<Ipv6, BC>
1710        + NudIpHandler<Ipv6, BC>
1711        + IpLayerHandler<Ipv6, BC>
1712        + CounterContext<IcmpRxCounters<Ipv6>>
1713        + CounterContext<IcmpTxCounters<Ipv6>>
1714        + CounterContext<NdpCounters>,
1715> IpTransportContext<Ipv6, BC, CC> for IcmpIpTransportContext
1716{
1717    fn receive_icmp_error(
1718        core_ctx: &mut CC,
1719        bindings_ctx: &mut BC,
1720        device: &CC::DeviceId,
1721        original_src_ip: Option<SpecifiedAddr<Ipv6Addr>>,
1722        original_dst_ip: SpecifiedAddr<Ipv6Addr>,
1723        original_body: &[u8],
1724        err: Icmpv6ErrorCode,
1725    ) {
1726        receive_ip_transport_icmp_error(
1727            core_ctx,
1728            bindings_ctx,
1729            device,
1730            original_src_ip,
1731            original_dst_ip,
1732            original_body,
1733            err,
1734        )
1735    }
1736
1737    fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<Ipv6>>(
1738        core_ctx: &mut CC,
1739        bindings_ctx: &mut BC,
1740        device: &CC::DeviceId,
1741        src_ip: Ipv6SourceAddr,
1742        dst_ip: SpecifiedAddr<Ipv6Addr>,
1743        mut buffer: B,
1744        info: &LocalDeliveryPacketInfo<Ipv6, H>,
1745    ) -> Result<(), (B, TransportReceiveError)> {
1746        let LocalDeliveryPacketInfo { meta, header_info, marks } = info;
1747        let ReceiveIpPacketMeta { broadcast: _, transparent_override } = meta;
1748        if let Some(delivery) = transparent_override {
1749            unreachable!(
1750                "cannot perform transparent local delivery {delivery:?} to an ICMP socket; \
1751                transparent proxy rules can only be configured for TCP and UDP packets"
1752            );
1753        }
1754
1755        trace!(
1756            "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet({:?}, {})",
1757            src_ip, dst_ip
1758        );
1759
1760        let packet = match buffer
1761            .parse_with::<_, Icmpv6Packet<_>>(IcmpParseArgs::new(src_ip.get(), dst_ip))
1762        {
1763            Ok(packet) => packet,
1764            Err(_) => return Ok(()), // TODO(joshlf): Do something else here?
1765        };
1766
1767        match packet {
1768            Icmpv6Packet::EchoRequest(echo_request) => {
1769                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx).echo_request.increment();
1770
1771                if let Some(src_ip) = SocketIpAddr::new_from_ipv6_source(src_ip) {
1772                    match SocketIpAddr::try_from(dst_ip) {
1773                        Ok(dst_ip) => {
1774                            let req = *echo_request.message();
1775                            let code = echo_request.code();
1776                            let (local_ip, remote_ip) = (dst_ip, src_ip);
1777                            debug!(
1778                                "replying to ICMP echo request from {remote_ip}: id={}, seq={}",
1779                                req.id(),
1780                                req.seq()
1781                            );
1782                            send_icmp_reply(
1783                                core_ctx,
1784                                bindings_ctx,
1785                                device,
1786                                remote_ip,
1787                                local_ip,
1788                                |src_ip| {
1789                                    IcmpPacketBuilder::<Ipv6, _>::new(
1790                                        src_ip,
1791                                        remote_ip.addr(),
1792                                        code,
1793                                        req.reply(),
1794                                    )
1795                                    .wrap_body(buffer)
1796                                },
1797                                &WithMarks(marks),
1798                            );
1799                        }
1800                        Err(AddrIsMappedError {}) => {
1801                            trace!(
1802                                "IpTransportContext<Ipv6>::receive_ip_packet: Received echo request with an ipv4-mapped-ipv6 destination address"
1803                            );
1804                        }
1805                    }
1806                } else {
1807                    trace!(
1808                        "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received echo request with an unspecified source address"
1809                    );
1810                }
1811            }
1812            Icmpv6Packet::EchoReply(echo_reply) => {
1813                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx).echo_reply.increment();
1814                trace!(
1815                    "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received an EchoReply message"
1816                );
1817                let parse_metadata = echo_reply.parse_metadata();
1818                buffer.undo_parse(parse_metadata);
1819                return <CC::EchoTransportContext
1820                            as IpTransportContext<Ipv6, BC, CC>>::receive_ip_packet(
1821                        core_ctx,
1822                        bindings_ctx,
1823                        device,
1824                        src_ip,
1825                        dst_ip,
1826                        buffer,
1827                        info
1828                );
1829            }
1830            Icmpv6Packet::Ndp(packet) => {
1831                receive_ndp_packet(core_ctx, bindings_ctx, device, src_ip, packet, header_info)
1832            }
1833            Icmpv6Packet::PacketTooBig(packet_too_big) => {
1834                CounterContext::<IcmpRxCounters<Ipv6>>::counters(core_ctx)
1835                    .packet_too_big
1836                    .increment();
1837                trace!(
1838                    "<IcmpIpTransportContext as IpTransportContext<Ipv6>>::receive_ip_packet: Received a Packet Too Big message"
1839                );
1840                if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
1841                    // We are updating the path MTU from the destination address
1842                    // of this `packet` (which is an IP address on this node) to
1843                    // some remote (identified by the source address of this
1844                    // `packet`).
1845                    //
1846                    // `update_pmtu_if_less` will only update the PMTU if the
1847                    // Packet Too Big message's MTU field had a value that was
1848                    // at least the IPv6 minimum MTU (which is required by IPv6
1849                    // RFC 8200).
1850                    let mtu = core_ctx.update_pmtu_if_less(
1851                        bindings_ctx,
1852                        dst_ip.get(),
1853                        src_ip.get(),
1854                        Mtu::new(packet_too_big.message().mtu()),
1855                    );
1856                    if let Some(mtu) = mtu {
1857                        receive_icmpv6_error(
1858                            core_ctx,
1859                            bindings_ctx,
1860                            device,
1861                            &packet_too_big,
1862                            Icmpv6ErrorCode::PacketTooBig(mtu),
1863                        );
1864                    }
1865                }
1866            }
1867            Icmpv6Packet::Mld(packet) => {
1868                core_ctx.receive_mld_packet(
1869                    bindings_ctx,
1870                    &device,
1871                    src_ip,
1872                    dst_ip,
1873                    packet,
1874                    header_info,
1875                );
1876            }
1877            Icmpv6Packet::DestUnreachable(dest_unreachable) => receive_icmpv6_error(
1878                core_ctx,
1879                bindings_ctx,
1880                device,
1881                &dest_unreachable,
1882                Icmpv6ErrorCode::DestUnreachable(dest_unreachable.code()),
1883            ),
1884            Icmpv6Packet::TimeExceeded(time_exceeded) => receive_icmpv6_error(
1885                core_ctx,
1886                bindings_ctx,
1887                device,
1888                &time_exceeded,
1889                Icmpv6ErrorCode::TimeExceeded(time_exceeded.code()),
1890            ),
1891            Icmpv6Packet::ParameterProblem(parameter_problem) => receive_icmpv6_error(
1892                core_ctx,
1893                bindings_ctx,
1894                device,
1895                &parameter_problem,
1896                Icmpv6ErrorCode::ParameterProblem(parameter_problem.code()),
1897            ),
1898        }
1899
1900        Ok(())
1901    }
1902}
1903
1904#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1905struct WithMarks<'a>(&'a Marks);
1906
1907impl<'a> OptionDelegationMarker for WithMarks<'a> {}
1908
1909impl<'a, I: IpExt> DelegatedRouteResolutionOptions<I> for WithMarks<'a> {
1910    fn marks(&self) -> &Marks {
1911        let Self(marks) = self;
1912        marks
1913    }
1914}
1915
1916impl<'a, I: IpExt> DelegatedSendOptions<I> for WithMarks<'a> {}
1917
1918/// Sends an ICMP reply to a remote host.
1919///
1920/// `send_icmp_reply` sends a reply to a non-error message (e.g., "echo request"
1921/// or "timestamp request" messages).
1922///
1923/// `get_body_from_src_ip` returns a `Serializer` with the bytes of the ICMP
1924/// packet, and, when called, is given the source IP address chosen for the
1925/// outbound packet. This allows `get_body_from_src_ip` to properly compute the
1926/// ICMP checksum, which relies on both the source and destination IP addresses
1927/// of the IP packet it's encapsulated in.
1928fn send_icmp_reply<I, BC, CC, S, F, O>(
1929    core_ctx: &mut CC,
1930    bindings_ctx: &mut BC,
1931    device: &CC::DeviceId,
1932    original_src_ip: SocketIpAddr<I::Addr>,
1933    original_dst_ip: SocketIpAddr<I::Addr>,
1934    get_body_from_src_ip: F,
1935    ip_options: &O,
1936) where
1937    I: IpExt + FilterIpExt,
1938    CC: IpSocketHandler<I, BC> + DeviceIdContext<AnyDevice> + CounterContext<IcmpTxCounters<I>>,
1939    BC: TxMetadataBindingsTypes,
1940    S: DynamicTransportSerializer<I>,
1941    F: FnOnce(SpecifiedAddr<I::Addr>) -> S,
1942    O: SendOptions<I> + RouteResolutionOptions<I>,
1943{
1944    trace!("send_icmp_reply({:?}, {}, {})", device, original_src_ip, original_dst_ip);
1945    core_ctx.counters().reply.increment();
1946    let tx_metadata: BC::TxMetadata = Default::default();
1947
1948    // Force the egress device if the original destination is multicast or
1949    // requires a zone (i.e. link-local non-loopback), ensuring we pick the
1950    // correct return route.
1951    let egress_device = (original_dst_ip.as_ref().is_multicast()
1952        || original_dst_ip.as_ref().must_have_zone())
1953    .then_some(EitherDeviceId::Strong(device));
1954
1955    core_ctx
1956        .send_oneshot_ip_packet_with_dyn_serializer(
1957            bindings_ctx,
1958            IpSocketArgs {
1959                device: egress_device,
1960                local_ip: IpDeviceAddr::new_from_socket_ip_addr(original_dst_ip),
1961                remote_ip: original_src_ip,
1962                proto: I::ICMP_IP_PROTO,
1963                options: ip_options,
1964            },
1965            tx_metadata,
1966            |src_ip| get_body_from_src_ip(src_ip.into()),
1967        )
1968        .unwrap_or_else(|err| {
1969            debug!("failed to send ICMP reply: {}", err);
1970        })
1971}
1972
1973/// Receive an ICMP(v4) error message.
1974///
1975/// `receive_icmpv4_error` handles an incoming ICMP error message by parsing the
1976/// original IPv4 packet and then delegating to the context.
1977fn receive_icmpv4_error<
1978    BC: IcmpBindingsContext,
1979    CC: InnerIcmpv4Context<BC>,
1980    B: SplitByteSlice,
1981    M: IcmpMessage<Ipv4, Body<B> = OriginalPacket<B>>,
1982>(
1983    core_ctx: &mut CC,
1984    bindings_ctx: &mut BC,
1985    device: &CC::DeviceId,
1986    packet: &IcmpPacket<Ipv4, B, M>,
1987    err: Icmpv4ErrorCode,
1988) {
1989    packet.with_original_packet(|res| match res {
1990        Ok(original_packet) => {
1991            let dst_ip = match SpecifiedAddr::new(original_packet.dst_ip()) {
1992                Some(ip) => ip,
1993                None => {
1994                    trace!("receive_icmpv4_error: Got ICMP error message whose original IPv4 packet contains an unspecified destination address; discarding");
1995                    return;
1996                },
1997            };
1998            InnerIcmpContext::receive_icmp_error(
1999                core_ctx,
2000                bindings_ctx,
2001                device,
2002                SpecifiedAddr::new(original_packet.src_ip()),
2003                dst_ip,
2004                original_packet.proto(),
2005                original_packet.body().into_inner(),
2006                err,
2007            );
2008        }
2009        Err(_) => debug!(
2010            "receive_icmpv4_error: Got ICMP error message with unparsable original IPv4 packet"
2011        ),
2012    })
2013}
2014
2015/// Receive an ICMPv6 error message.
2016///
2017/// `receive_icmpv6_error` handles an incoming ICMPv6 error message by parsing
2018/// the original IPv6 packet and then delegating to the context.
2019fn receive_icmpv6_error<
2020    BC: IcmpBindingsContext,
2021    CC: InnerIcmpv6Context<BC>,
2022    B: SplitByteSlice,
2023    M: IcmpMessage<Ipv6, Body<B> = OriginalPacket<B>>,
2024>(
2025    core_ctx: &mut CC,
2026    bindings_ctx: &mut BC,
2027    device: &CC::DeviceId,
2028    packet: &IcmpPacket<Ipv6, B, M>,
2029    err: Icmpv6ErrorCode,
2030) {
2031    packet.with_original_packet(|res| match res {
2032        Ok(original_packet) => {
2033            let dst_ip = match SpecifiedAddr::new(original_packet.dst_ip()) {
2034                Some(ip)=>ip,
2035                None => {
2036                    trace!("receive_icmpv6_error: Got ICMP error message whose original IPv6 packet contains an unspecified destination address; discarding");
2037                    return;
2038                },
2039            };
2040            match original_packet.body_proto() {
2041                Ok((body, proto)) => {
2042                    InnerIcmpContext::receive_icmp_error(
2043                        core_ctx,
2044                        bindings_ctx,
2045                        device,
2046                        SpecifiedAddr::new(original_packet.src_ip()),
2047                        dst_ip,
2048                        proto,
2049                        body.into_inner(),
2050                        err,
2051                    );
2052                }
2053                Err(ExtHdrParseError) => {
2054                    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");
2055                    // There's nothing we can do in this case, so we just
2056                    // return.
2057                    return;
2058                }
2059            }
2060        }
2061        Err(_body) => debug!(
2062            "receive_icmpv6_error: Got ICMPv6 error message with unparsable original IPv6 packet"
2063        ),
2064    })
2065}
2066
2067/// Send an ICMP(v4) message in response to receiving a packet destined for an
2068/// unreachable address.
2069///
2070/// `send_icmpv4_host_unreachable` sends the appropriate ICMP message in
2071/// response to receiving an IP packet from `src_ip` to `dst_ip`, where
2072/// `dst_ip` is unreachable. In particular, this is an ICMP
2073/// "destination unreachable" message with a "host unreachable" code.
2074///
2075/// `original_packet` must be an initial fragment or a complete IP
2076/// packet, per [RFC 792 Introduction]:
2077///
2078///   Also ICMP messages are only sent about errors in handling fragment zero of
2079///   fragemented [sic] datagrams.
2080///
2081/// `header_len` is the length of the header including all options.
2082///
2083/// [RFC 792 Introduction]: https://datatracker.ietf.org/doc/html/rfc792
2084pub fn send_icmpv4_host_unreachable<
2085    B: BufferMut,
2086    BC: IcmpBindingsContext,
2087    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2088>(
2089    core_ctx: &mut CC,
2090    bindings_ctx: &mut BC,
2091    device: Option<&CC::DeviceId>,
2092    frame_dst: Option<FrameDestination>,
2093    src_ip: SocketIpAddr<Ipv4Addr>,
2094    dst_ip: SocketIpAddr<Ipv4Addr>,
2095    original_packet: B,
2096    header_len: usize,
2097    fragment_type: Ipv4FragmentType,
2098    marks: &Marks,
2099) {
2100    core_ctx.counters().address_unreachable.increment();
2101
2102    send_icmpv4_dest_unreachable(
2103        core_ctx,
2104        bindings_ctx,
2105        device,
2106        frame_dst,
2107        src_ip,
2108        dst_ip,
2109        Icmpv4DestUnreachableCode::DestHostUnreachable,
2110        original_packet,
2111        header_len,
2112        fragment_type,
2113        marks,
2114    );
2115}
2116
2117/// Send an ICMPv6 message in response to receiving a packet destined for an
2118/// unreachable address.
2119///
2120/// `send_icmpv6_address_unreachable` sends the appropriate ICMP message in
2121/// response to receiving an IP packet from `src_ip` to `dst_ip`, where
2122/// `dst_ip` is unreachable. In particular, this is an ICMP
2123/// "destination unreachable" message with an "address unreachable" code.
2124///
2125/// `original_packet` contains the contents of the entire original packet,
2126/// including extension headers.
2127pub fn send_icmpv6_address_unreachable<
2128    B: BufferMut,
2129    BC: IcmpBindingsContext,
2130    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2131>(
2132    core_ctx: &mut CC,
2133    bindings_ctx: &mut BC,
2134    device: Option<&CC::DeviceId>,
2135    frame_dst: Option<FrameDestination>,
2136    src_ip: SocketIpAddr<Ipv6Addr>,
2137    dst_ip: SocketIpAddr<Ipv6Addr>,
2138    original_packet: B,
2139    marks: &Marks,
2140) {
2141    core_ctx.counters().address_unreachable.increment();
2142
2143    send_icmpv6_dest_unreachable(
2144        core_ctx,
2145        bindings_ctx,
2146        device,
2147        frame_dst,
2148        src_ip,
2149        dst_ip,
2150        Icmpv6DestUnreachableCode::AddrUnreachable,
2151        original_packet,
2152        marks,
2153    );
2154}
2155
2156/// Send an ICMP(v4) message in response to receiving a packet destined for an
2157/// unsupported IPv4 protocol.
2158///
2159/// `send_icmpv4_protocol_unreachable` sends the appropriate ICMP message in
2160/// response to receiving an IP packet from `src_ip` to `dst_ip` identifying an
2161/// unsupported protocol - in particular, a "destination unreachable" message
2162/// with a "protocol unreachable" code.
2163///
2164/// `original_packet` contains the contents of the entire original packet,
2165/// including the IP header. This must be a whole packet, not a packet fragment.
2166/// `header_len` is the length of the header including all options.
2167pub(crate) fn send_icmpv4_protocol_unreachable<
2168    B: BufferMut,
2169    BC: IcmpBindingsContext,
2170    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2171>(
2172    core_ctx: &mut CC,
2173    bindings_ctx: &mut BC,
2174    device: &CC::DeviceId,
2175    frame_dst: Option<FrameDestination>,
2176    src_ip: SocketIpAddr<Ipv4Addr>,
2177    dst_ip: SocketIpAddr<Ipv4Addr>,
2178    original_packet: B,
2179    header_len: usize,
2180    marks: &Marks,
2181) {
2182    core_ctx.counters().protocol_unreachable.increment();
2183
2184    send_icmpv4_dest_unreachable(
2185        core_ctx,
2186        bindings_ctx,
2187        Some(device),
2188        frame_dst,
2189        src_ip,
2190        dst_ip,
2191        Icmpv4DestUnreachableCode::DestProtocolUnreachable,
2192        original_packet,
2193        header_len,
2194        // If we are sending a protocol unreachable error it is correct to assume that, if the
2195        // packet was initially fragmented, it has been successfully reassembled by now. It
2196        // guarantees that we won't send more than one ICMP Destination Unreachable message for
2197        // different fragments of the same original packet, so we should behave as if we are
2198        // handling an initial fragment.
2199        Ipv4FragmentType::InitialFragment,
2200        marks,
2201    );
2202}
2203
2204/// Send an ICMPv6 message in response to receiving a packet destined for an
2205/// unsupported Next Header.
2206///
2207/// `send_icmpv6_protocol_unreachable` is like
2208/// [`send_icmpv4_protocol_unreachable`], but for ICMPv6. It sends an ICMPv6
2209/// "parameter problem" message with an "unrecognized next header type" code.
2210///
2211/// `header_len` is the length of all IPv6 headers (including extension headers)
2212/// *before* the payload with the problematic Next Header type.
2213pub(crate) fn send_icmpv6_protocol_unreachable<
2214    B: BufferMut,
2215    BC: IcmpBindingsContext,
2216    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2217>(
2218    core_ctx: &mut CC,
2219    bindings_ctx: &mut BC,
2220    device: &CC::DeviceId,
2221    frame_dst: Option<FrameDestination>,
2222    src_ip: SocketIpAddr<Ipv6Addr>,
2223    dst_ip: SocketIpAddr<Ipv6Addr>,
2224    original_packet: B,
2225    header_len: usize,
2226    marks: &Marks,
2227) {
2228    core_ctx.counters().protocol_unreachable.increment();
2229
2230    send_icmpv6_parameter_problem(
2231        core_ctx,
2232        bindings_ctx,
2233        device,
2234        frame_dst,
2235        src_ip,
2236        dst_ip,
2237        Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
2238        // Per RFC 4443, the pointer refers to the first byte of the packet
2239        // whose Next Header field was unrecognized. It is measured as an offset
2240        // from the beginning of the first IPv6 header. E.g., a pointer of 40
2241        // (the length of a single IPv6 header) would indicate that the Next
2242        // Header field from that header - and hence of the first encapsulated
2243        // packet - was unrecognized.
2244        //
2245        // NOTE: Since header_len is a usize, this could theoretically be a
2246        // lossy conversion. However, all that means in practice is that, if a
2247        // remote host somehow managed to get us to process a frame with a 4GB
2248        // IP header and send an ICMP response, the pointer value would be
2249        // wrong. It's not worth wasting special logic to avoid generating a
2250        // malformed packet in a case that will almost certainly never happen.
2251        Icmpv6ParameterProblem::new(header_len as u32),
2252        original_packet,
2253        false,
2254        marks,
2255    );
2256}
2257
2258/// Send an ICMP(v4) message in response to receiving a packet destined for an
2259/// unreachable local transport-layer port.
2260///
2261/// `send_icmpv4_port_unreachable` sends the appropriate ICMP message in
2262/// response to receiving an IP packet from `src_ip` to `dst_ip` with an
2263/// unreachable local transport-layer port. In particular, this is an ICMP
2264/// "destination unreachable" message with a "port unreachable" code.
2265///
2266/// `original_packet` contains the contents of the entire original packet,
2267/// including the IP header. This must be a whole packet, not a packet fragment.
2268/// `header_len` is the length of the header including all options.
2269pub(crate) fn send_icmpv4_port_unreachable<
2270    B: BufferMut,
2271    BC: IcmpBindingsContext,
2272    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2273>(
2274    core_ctx: &mut CC,
2275    bindings_ctx: &mut BC,
2276    device: &CC::DeviceId,
2277    frame_dst: Option<FrameDestination>,
2278    src_ip: SocketIpAddr<Ipv4Addr>,
2279    dst_ip: SocketIpAddr<Ipv4Addr>,
2280    original_packet: B,
2281    header_len: usize,
2282    marks: &Marks,
2283) {
2284    core_ctx.counters().port_unreachable.increment();
2285
2286    send_icmpv4_dest_unreachable(
2287        core_ctx,
2288        bindings_ctx,
2289        Some(device),
2290        frame_dst,
2291        src_ip,
2292        dst_ip,
2293        Icmpv4DestUnreachableCode::DestPortUnreachable,
2294        original_packet,
2295        header_len,
2296        // If we are sending a port unreachable error it is correct to assume that, if the packet
2297        // was initially fragmented, it has been successfully reassembled by now. It guarantees that
2298        // we won't send more than one ICMP Destination Unreachable message for different fragments
2299        // of the same original packet, so we should behave as if we are handling an initial
2300        // fragment.
2301        Ipv4FragmentType::InitialFragment,
2302        marks,
2303    );
2304}
2305
2306/// Send an ICMPv6 message in response to receiving a packet destined for an
2307/// unreachable local transport-layer port.
2308///
2309/// `send_icmpv6_port_unreachable` is like [`send_icmpv4_port_unreachable`], but
2310/// for ICMPv6.
2311///
2312/// `original_packet` contains the contents of the entire original packet,
2313/// including extension headers.
2314pub(crate) fn send_icmpv6_port_unreachable<
2315    B: BufferMut,
2316    BC: IcmpBindingsContext,
2317    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2318>(
2319    core_ctx: &mut CC,
2320    bindings_ctx: &mut BC,
2321    device: &CC::DeviceId,
2322    frame_dst: Option<FrameDestination>,
2323    src_ip: SocketIpAddr<Ipv6Addr>,
2324    dst_ip: SocketIpAddr<Ipv6Addr>,
2325    original_packet: B,
2326    marks: &Marks,
2327) {
2328    core_ctx.counters().port_unreachable.increment();
2329
2330    send_icmpv6_dest_unreachable(
2331        core_ctx,
2332        bindings_ctx,
2333        Some(device),
2334        frame_dst,
2335        src_ip,
2336        dst_ip,
2337        Icmpv6DestUnreachableCode::PortUnreachable,
2338        original_packet,
2339        marks,
2340    );
2341}
2342
2343/// Send an ICMP(v4) message in response to receiving a packet destined for an
2344/// unreachable network.
2345///
2346/// `send_icmpv4_net_unreachable` sends the appropriate ICMP message in response
2347/// to receiving an IP packet from `src_ip` to an unreachable `dst_ip`. In
2348/// particular, this is an ICMP "destination unreachable" message with a "net
2349/// unreachable" code.
2350///
2351/// `original_packet` contains the contents of the entire original packet -
2352/// including all IP headers. `header_len` is the length of the IPv4 header. It
2353/// is ignored for IPv6.
2354pub(crate) fn send_icmpv4_net_unreachable<
2355    B: BufferMut,
2356    BC: IcmpBindingsContext,
2357    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2358>(
2359    core_ctx: &mut CC,
2360    bindings_ctx: &mut BC,
2361    device: &CC::DeviceId,
2362    frame_dst: Option<FrameDestination>,
2363    src_ip: SocketIpAddr<Ipv4Addr>,
2364    dst_ip: SocketIpAddr<Ipv4Addr>,
2365    proto: Ipv4Proto,
2366    original_packet: B,
2367    header_len: usize,
2368    fragment_type: Ipv4FragmentType,
2369    marks: &Marks,
2370) {
2371    core_ctx.counters().net_unreachable.increment();
2372
2373    // Check whether we MUST NOT send an ICMP error message
2374    // because the original packet was itself an ICMP error message.
2375    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
2376        return;
2377    }
2378
2379    send_icmpv4_dest_unreachable(
2380        core_ctx,
2381        bindings_ctx,
2382        Some(device),
2383        frame_dst,
2384        src_ip,
2385        dst_ip,
2386        Icmpv4DestUnreachableCode::DestNetworkUnreachable,
2387        original_packet,
2388        header_len,
2389        fragment_type,
2390        marks,
2391    );
2392}
2393
2394/// Send an ICMPv6 message in response to receiving a packet destined for an
2395/// unreachable network.
2396///
2397/// `send_icmpv6_net_unreachable` is like [`send_icmpv4_net_unreachable`], but
2398/// for ICMPv6. It sends an ICMPv6 "destination unreachable" message with a "no
2399/// route to destination" code.
2400///
2401/// `original_packet` contains the contents of the entire original packet
2402/// including extension headers. `header_len` is the length of the IP header and
2403/// all extension headers.
2404pub(crate) fn send_icmpv6_net_unreachable<
2405    B: BufferMut,
2406    BC: IcmpBindingsContext,
2407    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2408>(
2409    core_ctx: &mut CC,
2410    bindings_ctx: &mut BC,
2411    device: &CC::DeviceId,
2412    frame_dst: Option<FrameDestination>,
2413    src_ip: SocketIpAddr<Ipv6Addr>,
2414    dst_ip: SocketIpAddr<Ipv6Addr>,
2415    proto: Ipv6Proto,
2416    original_packet: B,
2417    header_len: usize,
2418    marks: &Marks,
2419) {
2420    core_ctx.counters().net_unreachable.increment();
2421
2422    // Check whether we MUST NOT send an ICMP error message
2423    // because the original packet was itself an ICMP error message.
2424    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2425        return;
2426    }
2427
2428    send_icmpv6_dest_unreachable(
2429        core_ctx,
2430        bindings_ctx,
2431        Some(device),
2432        frame_dst,
2433        src_ip,
2434        dst_ip,
2435        Icmpv6DestUnreachableCode::NoRoute,
2436        original_packet,
2437        marks,
2438    );
2439}
2440
2441/// Send an ICMP(v4) message in response to receiving a packet whose TTL has
2442/// expired.
2443///
2444/// `send_icmpv4_ttl_expired` sends the appropriate ICMP in response to
2445/// receiving an IP packet from `src_ip` to `dst_ip` whose TTL has expired. In
2446/// particular, this is an ICMP "time exceeded" message with a "time to live
2447/// exceeded in transit" code.
2448///
2449/// `original_packet` contains the contents of the entire original packet,
2450/// including the header. `header_len` is the length of the IP header including
2451/// options.
2452pub(crate) fn send_icmpv4_ttl_expired<
2453    B: BufferMut,
2454    BC: IcmpBindingsContext,
2455    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2456>(
2457    core_ctx: &mut CC,
2458    bindings_ctx: &mut BC,
2459    device: &CC::DeviceId,
2460    frame_dst: Option<FrameDestination>,
2461    src_ip: SocketIpAddr<Ipv4Addr>,
2462    dst_ip: SocketIpAddr<Ipv4Addr>,
2463    proto: Ipv4Proto,
2464    original_packet: B,
2465    header_len: usize,
2466    fragment_type: Ipv4FragmentType,
2467    marks: &Marks,
2468) {
2469    core_ctx.counters().ttl_expired.increment();
2470
2471    // Check whether we MUST NOT send an ICMP error message because the original
2472    // packet was itself an ICMP error message.
2473    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
2474        return;
2475    }
2476
2477    send_icmpv4_error_message(
2478        core_ctx,
2479        bindings_ctx,
2480        Some(device),
2481        frame_dst,
2482        src_ip,
2483        dst_ip,
2484        Icmpv4ErrorMessage::TimeExceeded {
2485            message: IcmpTimeExceeded::default(),
2486            code: Icmpv4TimeExceededCode::TtlExpired,
2487        },
2488        original_packet,
2489        header_len,
2490        fragment_type,
2491        marks,
2492    )
2493}
2494
2495/// Send an ICMPv6 message in response to receiving a packet whose hop limit has
2496/// expired.
2497///
2498/// `send_icmpv6_ttl_expired` is like [`send_icmpv4_ttl_expired`], but for
2499/// ICMPv6. It sends an ICMPv6 "time exceeded" message with a "hop limit
2500/// exceeded in transit" code.
2501///
2502/// `original_packet` contains the contents of the entire original packet
2503/// including extension headers. `header_len` is the length of the IP header and
2504/// all extension headers.
2505pub(crate) fn send_icmpv6_ttl_expired<
2506    B: BufferMut,
2507    BC: IcmpBindingsContext,
2508    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2509>(
2510    core_ctx: &mut CC,
2511    bindings_ctx: &mut BC,
2512    device: &CC::DeviceId,
2513    frame_dst: Option<FrameDestination>,
2514    src_ip: SocketIpAddr<Ipv6Addr>,
2515    dst_ip: SocketIpAddr<Ipv6Addr>,
2516    proto: Ipv6Proto,
2517    original_packet: B,
2518    header_len: usize,
2519    marks: &Marks,
2520) {
2521    core_ctx.counters().ttl_expired.increment();
2522
2523    // Check whether we MUST NOT send an ICMP error message because the
2524    // original packet was itself an ICMP error message.
2525    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2526        return;
2527    }
2528
2529    send_icmpv6_error_message(
2530        core_ctx,
2531        bindings_ctx,
2532        Some(device),
2533        frame_dst,
2534        src_ip,
2535        dst_ip,
2536        Icmpv6ErrorMessage::TimeExceeded {
2537            message: IcmpTimeExceeded::default(),
2538            code: Icmpv6TimeExceededCode::HopLimitExceeded,
2539        },
2540        original_packet,
2541        false, /* allow_dst_multicast */
2542        marks,
2543    )
2544}
2545
2546// TODO(joshlf): Test send_icmpv6_packet_too_big once we support fake IPv6 test
2547// setups.
2548
2549/// Send an ICMPv6 message in response to receiving a packet whose size exceeds
2550/// the MTU of the next hop interface.
2551///
2552/// `send_icmpv6_packet_too_big` sends an ICMPv6 "packet too big" message in
2553/// response to receiving an IP packet from `src_ip` to `dst_ip` whose size
2554/// exceeds the `mtu` of the next hop interface.
2555pub(crate) fn send_icmpv6_packet_too_big<
2556    B: BufferMut,
2557    BC: IcmpBindingsContext,
2558    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2559>(
2560    core_ctx: &mut CC,
2561    bindings_ctx: &mut BC,
2562    device: &CC::DeviceId,
2563    frame_dst: Option<FrameDestination>,
2564    src_ip: SocketIpAddr<Ipv6Addr>,
2565    dst_ip: SocketIpAddr<Ipv6Addr>,
2566    proto: Ipv6Proto,
2567    mtu: Mtu,
2568    original_packet: B,
2569    header_len: usize,
2570    marks: &Marks,
2571) {
2572    core_ctx.counters().packet_too_big.increment();
2573    // Check whether we MUST NOT send an ICMP error message because the
2574    // original packet was itself an ICMP error message.
2575    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
2576        return;
2577    }
2578
2579    send_icmpv6_error_message(
2580        core_ctx,
2581        bindings_ctx,
2582        Some(device),
2583        frame_dst,
2584        src_ip,
2585        dst_ip,
2586        Icmpv6ErrorMessage::PacketTooBig {
2587            message: Icmpv6PacketTooBig::new(mtu.into()),
2588            code: IcmpZeroCode,
2589        },
2590        original_packet,
2591        // As per RFC 4443 section 2.4.e,
2592        //
2593        //   An ICMPv6 error message MUST NOT be originated as a result of
2594        //   receiving the following:
2595        //
2596        //     (e.3) A packet destined to an IPv6 multicast address.  (There are
2597        //           two exceptions to this rule: (1) the Packet Too Big Message
2598        //           (Section 3.2) to allow Path MTU discovery to work for IPv6
2599        //           multicast, and (2) the Parameter Problem Message, Code 2
2600        //           (Section 3.4) reporting an unrecognized IPv6 option (see
2601        //           Section 4.2 of [IPv6]) that has the Option Type highest-
2602        //           order two bits set to 10).
2603        //
2604        //     (e.4) A packet sent as a link-layer multicast (the exceptions
2605        //           from e.3 apply to this case, too).
2606        //
2607        // Thus, we explicitly allow sending a Packet Too Big error if the
2608        // destination was a multicast packet.
2609        true, /* allow_dst_multicast */
2610        marks,
2611    )
2612}
2613
2614pub(crate) fn send_icmpv4_parameter_problem<
2615    B: BufferMut,
2616    BC: IcmpBindingsContext,
2617    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2618>(
2619    core_ctx: &mut CC,
2620    bindings_ctx: &mut BC,
2621    device: &CC::DeviceId,
2622    frame_dst: Option<FrameDestination>,
2623    src_ip: SocketIpAddr<Ipv4Addr>,
2624    dst_ip: SocketIpAddr<Ipv4Addr>,
2625    code: Icmpv4ParameterProblemCode,
2626    parameter_problem: Icmpv4ParameterProblem,
2627    original_packet: B,
2628    header_len: usize,
2629    fragment_type: Ipv4FragmentType,
2630    marks: &Marks,
2631) {
2632    core_ctx.counters().parameter_problem.increment();
2633
2634    send_icmpv4_error_message(
2635        core_ctx,
2636        bindings_ctx,
2637        Some(device),
2638        frame_dst,
2639        src_ip,
2640        dst_ip,
2641        Icmpv4ErrorMessage::ParameterProblem { message: parameter_problem, code },
2642        original_packet,
2643        header_len,
2644        fragment_type,
2645        marks,
2646    )
2647}
2648
2649/// Send an ICMPv6 Parameter Problem error message.
2650///
2651/// If the error message is Code 2 reporting an unrecognized IPv6 option that
2652/// has the Option Type highest-order two bits set to 10, `allow_dst_multicast`
2653/// must be set to `true`. See [`should_send_icmpv6_error`] for more details.
2654///
2655/// # Panics
2656///
2657/// Panics if `allow_multicast_addr` is set to `true`, but this Parameter
2658/// Problem's code is not 2 (Unrecognized IPv6 Option).
2659pub(crate) fn send_icmpv6_parameter_problem<
2660    B: BufferMut,
2661    BC: IcmpBindingsContext,
2662    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2663>(
2664    core_ctx: &mut CC,
2665    bindings_ctx: &mut BC,
2666    device: &CC::DeviceId,
2667    frame_dst: Option<FrameDestination>,
2668    src_ip: SocketIpAddr<Ipv6Addr>,
2669    dst_ip: SocketIpAddr<Ipv6Addr>,
2670    code: Icmpv6ParameterProblemCode,
2671    parameter_problem: Icmpv6ParameterProblem,
2672    original_packet: B,
2673    allow_dst_multicast: bool,
2674    marks: &Marks,
2675) {
2676    // Only allow the `allow_dst_multicast` parameter to be set if the code is
2677    // the unrecognized IPv6 option as that is one of the few exceptions where
2678    // we can send an ICMP packet in response to a packet that was destined for
2679    // a multicast address.
2680    assert!(!allow_dst_multicast || code == Icmpv6ParameterProblemCode::UnrecognizedIpv6Option);
2681
2682    core_ctx.counters().parameter_problem.increment();
2683
2684    send_icmpv6_error_message(
2685        core_ctx,
2686        bindings_ctx,
2687        Some(device),
2688        frame_dst,
2689        src_ip,
2690        dst_ip,
2691        Icmpv6ErrorMessage::ParameterProblem { message: parameter_problem, code },
2692        original_packet,
2693        allow_dst_multicast,
2694        marks,
2695    )
2696}
2697
2698fn send_icmpv4_dest_unreachable<
2699    B: BufferMut,
2700    BC: IcmpBindingsContext,
2701    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2702>(
2703    core_ctx: &mut CC,
2704    bindings_ctx: &mut BC,
2705    device: Option<&CC::DeviceId>,
2706    frame_dst: Option<FrameDestination>,
2707    src_ip: SocketIpAddr<Ipv4Addr>,
2708    dst_ip: SocketIpAddr<Ipv4Addr>,
2709    code: Icmpv4DestUnreachableCode,
2710    original_packet: B,
2711    header_len: usize,
2712    fragment_type: Ipv4FragmentType,
2713    marks: &Marks,
2714) {
2715    core_ctx.counters().dest_unreachable.increment();
2716    send_icmpv4_error_message(
2717        core_ctx,
2718        bindings_ctx,
2719        device,
2720        frame_dst,
2721        src_ip,
2722        dst_ip,
2723        Icmpv4ErrorMessage::DestUnreachable { message: IcmpDestUnreachable::default(), code },
2724        original_packet,
2725        header_len,
2726        fragment_type,
2727        marks,
2728    )
2729}
2730
2731fn send_icmpv6_dest_unreachable<
2732    B: BufferMut,
2733    BC: IcmpBindingsContext,
2734    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2735>(
2736    core_ctx: &mut CC,
2737    bindings_ctx: &mut BC,
2738    device: Option<&CC::DeviceId>,
2739    frame_dst: Option<FrameDestination>,
2740    src_ip: SocketIpAddr<Ipv6Addr>,
2741    dst_ip: SocketIpAddr<Ipv6Addr>,
2742    code: Icmpv6DestUnreachableCode,
2743    original_packet: B,
2744    marks: &Marks,
2745) {
2746    send_icmpv6_error_message(
2747        core_ctx,
2748        bindings_ctx,
2749        device,
2750        frame_dst,
2751        src_ip,
2752        dst_ip,
2753        Icmpv6ErrorMessage::DestUnreachable { message: IcmpDestUnreachable::default(), code },
2754        original_packet,
2755        false, /* allow_dst_multicast */
2756        marks,
2757    )
2758}
2759
2760/// A type to allow implementing the required filtering traits on a concrete
2761/// subset of message types.
2762#[allow(missing_docs)]
2763enum Icmpv4ErrorMessage {
2764    TimeExceeded {
2765        message: IcmpTimeExceeded,
2766        code: <IcmpTimeExceeded as IcmpMessage<Ipv4>>::Code,
2767    },
2768    ParameterProblem {
2769        message: Icmpv4ParameterProblem,
2770        code: <Icmpv4ParameterProblem as IcmpMessage<Ipv4>>::Code,
2771    },
2772    DestUnreachable {
2773        message: IcmpDestUnreachable,
2774        code: <IcmpDestUnreachable as IcmpMessage<Ipv4>>::Code,
2775    },
2776}
2777
2778fn send_icmpv4_error_message<
2779    B: BufferMut,
2780    BC: IcmpBindingsContext,
2781    CC: InnerIcmpv4Context<BC> + CounterContext<IcmpTxCounters<Ipv4>>,
2782>(
2783    core_ctx: &mut CC,
2784    bindings_ctx: &mut BC,
2785    device: Option<&CC::DeviceId>,
2786    frame_dst: Option<FrameDestination>,
2787    original_src_ip: SocketIpAddr<Ipv4Addr>,
2788    original_dst_ip: SocketIpAddr<Ipv4Addr>,
2789    message: Icmpv4ErrorMessage,
2790    mut original_packet: B,
2791    header_len: usize,
2792    fragment_type: Ipv4FragmentType,
2793    marks: &Marks,
2794) {
2795    // TODO(https://fxbug.dev/42177876): Come up with rules for when to send ICMP
2796    // error messages.
2797
2798    if !should_send_icmpv4_error(
2799        frame_dst,
2800        original_src_ip.into(),
2801        original_dst_ip.into(),
2802        fragment_type,
2803    ) {
2804        return;
2805    }
2806
2807    // Per RFC 792, body contains entire IPv4 header + 64 bytes of original
2808    // body.
2809    original_packet.shrink_back_to(header_len + 64);
2810
2811    let tx_metadata: BC::TxMetadata = Default::default();
2812
2813    macro_rules! send {
2814        ($message:expr, $code:expr) => {{
2815            // TODO(https://fxbug.dev/42177877): Improve source address selection for ICMP
2816            // errors sent from unnumbered/router interfaces.
2817            let _ = try_send_error!(
2818                core_ctx,
2819                bindings_ctx,
2820                core_ctx.send_oneshot_ip_packet_with_dyn_serializer(
2821                    bindings_ctx,
2822                    IpSocketArgs {
2823                        device: device.map(EitherDeviceId::Strong),
2824                        local_ip: None,
2825                        remote_ip: original_src_ip,
2826                        proto: Ipv4Proto::Icmp,
2827                        options: &WithMarks(marks),
2828                    },
2829                    tx_metadata,
2830                    |local_ip| {
2831                        IcmpPacketBuilder::<Ipv4, _>::new(
2832                            local_ip.addr(),
2833                            original_src_ip.addr(),
2834                            $code,
2835                            $message,
2836                        )
2837                        .wrap_body(original_packet)
2838                    },
2839                )
2840            );
2841        }};
2842    }
2843
2844    match message {
2845        Icmpv4ErrorMessage::TimeExceeded { message, code } => send!(message, code),
2846        Icmpv4ErrorMessage::ParameterProblem { message, code } => send!(message, code),
2847        Icmpv4ErrorMessage::DestUnreachable { message, code } => send!(message, code),
2848    }
2849}
2850
2851/// A type to allow implementing the required filtering traits on a concrete
2852/// subset of message types.
2853#[allow(missing_docs)]
2854enum Icmpv6ErrorMessage {
2855    TimeExceeded {
2856        message: IcmpTimeExceeded,
2857        code: <IcmpTimeExceeded as IcmpMessage<Ipv6>>::Code,
2858    },
2859    PacketTooBig {
2860        message: Icmpv6PacketTooBig,
2861        code: <Icmpv6PacketTooBig as IcmpMessage<Ipv6>>::Code,
2862    },
2863    ParameterProblem {
2864        message: Icmpv6ParameterProblem,
2865        code: <Icmpv6ParameterProblem as IcmpMessage<Ipv6>>::Code,
2866    },
2867    DestUnreachable {
2868        message: IcmpDestUnreachable,
2869        code: <IcmpDestUnreachable as IcmpMessage<Ipv6>>::Code,
2870    },
2871}
2872
2873fn send_icmpv6_error_message<
2874    B: BufferMut,
2875    BC: IcmpBindingsContext,
2876    CC: InnerIcmpv6Context<BC> + CounterContext<IcmpTxCounters<Ipv6>>,
2877>(
2878    core_ctx: &mut CC,
2879    bindings_ctx: &mut BC,
2880    device: Option<&CC::DeviceId>,
2881    frame_dst: Option<FrameDestination>,
2882    original_src_ip: SocketIpAddr<Ipv6Addr>,
2883    original_dst_ip: SocketIpAddr<Ipv6Addr>,
2884    message: Icmpv6ErrorMessage,
2885    original_packet: B,
2886    allow_dst_multicast: bool,
2887    marks: &Marks,
2888) {
2889    // TODO(https://fxbug.dev/42177876): Come up with rules for when to send ICMP
2890    // error messages.
2891
2892    if !should_send_icmpv6_error(
2893        frame_dst,
2894        original_src_ip.into(),
2895        original_dst_ip.into(),
2896        allow_dst_multicast,
2897    ) {
2898        return;
2899    }
2900
2901    struct Icmpv6ErrorOptions<'a>(&'a Marks);
2902    impl<'a> OptionDelegationMarker for Icmpv6ErrorOptions<'a> {}
2903    impl<'a> DelegatedSendOptions<Ipv6> for Icmpv6ErrorOptions<'a> {
2904        fn mtu(&self) -> Mtu {
2905            Ipv6::MINIMUM_LINK_MTU
2906        }
2907    }
2908    impl<'a> DelegatedRouteResolutionOptions<Ipv6> for Icmpv6ErrorOptions<'a> {
2909        fn marks(&self) -> &Marks {
2910            let Self(marks) = self;
2911            marks
2912        }
2913    }
2914
2915    let tx_metadata: BC::TxMetadata = Default::default();
2916
2917    macro_rules! send {
2918        ($message:expr, $code:expr) => {{
2919            // TODO(https://fxbug.dev/42177877): Improve source address selection for ICMP
2920            // errors sent from unnumbered/router interfaces.
2921            let _ = try_send_error!(
2922                core_ctx,
2923                bindings_ctx,
2924                core_ctx.send_oneshot_ip_packet_with_dyn_serializer(
2925                    bindings_ctx,
2926                    IpSocketArgs {
2927                        device: device.map(EitherDeviceId::Strong),
2928                        local_ip: None,
2929                        remote_ip: original_src_ip,
2930                        proto: Ipv6Proto::Icmpv6,
2931                        options: &Icmpv6ErrorOptions(marks),
2932                    },
2933                    tx_metadata,
2934                    |local_ip| {
2935                        let icmp_builder = IcmpPacketBuilder::<Ipv6, _>::new(
2936                            local_ip.addr(),
2937                            original_src_ip.addr(),
2938                            $code,
2939                            $message,
2940                        );
2941
2942                        // Per RFC 4443, body contains as much of the original body as
2943                        // possible without exceeding IPv6 minimum MTU.
2944                        icmp_builder.wrap_body(TruncatingSerializer::new(
2945                            original_packet,
2946                            TruncateDirection::DiscardBack,
2947                        ))
2948                    },
2949                )
2950            );
2951        }};
2952    }
2953
2954    match message {
2955        Icmpv6ErrorMessage::TimeExceeded { message, code } => send!(message, code),
2956        Icmpv6ErrorMessage::PacketTooBig { message, code } => send!(message, code),
2957        Icmpv6ErrorMessage::ParameterProblem { message, code } => send!(message, code),
2958        Icmpv6ErrorMessage::DestUnreachable { message, code } => send!(message, code),
2959    }
2960}
2961
2962/// Should we send an ICMP(v4) response?
2963///
2964/// `should_send_icmpv4_error` implements the logic described in RFC 1122
2965/// Section 3.2.2. It decides whether, upon receiving an incoming packet with
2966/// the given parameters, we should send an ICMP response or not. In particular,
2967/// we do not send an ICMP response if we've received:
2968/// - a packet destined to a broadcast or multicast address
2969/// - a packet sent in a link-layer broadcast
2970/// - a non-initial fragment
2971/// - a packet whose source address does not define a single host (a
2972///   zero/unspecified address, a loopback address, a broadcast address, a
2973///   multicast address, or a Class E address)
2974///
2975/// Note that `should_send_icmpv4_error` does NOT check whether the incoming
2976/// packet contained an ICMP error message. This is because that check is
2977/// unnecessary for some ICMP error conditions. The ICMP error message check can
2978/// be performed separately with `is_icmp_error_message`.
2979fn should_send_icmpv4_error(
2980    frame_dst: Option<FrameDestination>,
2981    src_ip: SpecifiedAddr<Ipv4Addr>,
2982    dst_ip: SpecifiedAddr<Ipv4Addr>,
2983    fragment_type: Ipv4FragmentType,
2984) -> bool {
2985    // NOTE: We do not explicitly implement the "unspecified address" check, as
2986    // it is enforced by the types of the arguments.
2987
2988    // TODO(joshlf): Implement the rest of the rules:
2989    // - a packet destined to a subnet broadcast address
2990    // - a packet whose source address is a subnet broadcast address
2991
2992    // NOTE: The FrameDestination type has variants for unicast, multicast, and
2993    // broadcast. One implication of the fact that we only check for broadcast
2994    // here (in compliance with the RFC) is that we could, in one very unlikely
2995    // edge case, respond with an ICMP error message to an IP packet which was
2996    // sent in a link-layer multicast frame. In particular, that can happen if
2997    // we subscribe to a multicast IP group and, as a result, subscribe to the
2998    // corresponding multicast MAC address, and we receive a unicast IP packet
2999    // in a multicast link-layer frame destined to that MAC address.
3000    //
3001    // TODO(joshlf): Should we filter incoming multicast IP traffic to make sure
3002    // that it matches the multicast MAC address of the frame it was
3003    // encapsulated in?
3004    fragment_type == Ipv4FragmentType::InitialFragment
3005        && !(dst_ip.is_multicast()
3006            || dst_ip.is_limited_broadcast()
3007            || frame_dst.is_some_and(|dst| dst.is_broadcast())
3008            || src_ip.is_loopback()
3009            || src_ip.is_limited_broadcast()
3010            || src_ip.is_multicast()
3011            || src_ip.is_class_e())
3012}
3013
3014/// Should we send an ICMPv6 response?
3015///
3016/// `should_send_icmpv6_error` implements the logic described in RFC 4443
3017/// Section 2.4.e. It decides whether, upon receiving an incoming packet with
3018/// the given parameters, we should send an ICMP response or not. In particular,
3019/// we do not send an ICMP response if we've received:
3020/// - a packet destined to a multicast address
3021///   - Two exceptions to this rules:
3022///     1) the Packet Too Big Message to allow Path MTU discovery to work for
3023///        IPv6 multicast
3024///     2) the Parameter Problem Message, Code 2 reporting an unrecognized IPv6
3025///        option that has the Option Type highest-order two bits set to 10
3026/// - a packet sent as a link-layer multicast or broadcast
3027///   - same exceptions apply here as well.
3028/// - a packet whose source address does not define a single host (a
3029///   zero/unspecified address, a loopback address, or a multicast address)
3030///
3031/// If an ICMP response will be a Packet Too Big Message or a Parameter Problem
3032/// Message, Code 2 reporting an unrecognized IPv6 option that has the Option
3033/// Type highest-order two bits set to 10, `info.allow_dst_multicast` must be
3034/// set to `true` so this function will allow the exception mentioned above.
3035///
3036/// Note that `should_send_icmpv6_error` does NOT check whether the incoming
3037/// packet contained an ICMP error message. This is because that check is
3038/// unnecessary for some ICMP error conditions. The ICMP error message check can
3039/// be performed separately with `is_icmp_error_message`.
3040fn should_send_icmpv6_error(
3041    frame_dst: Option<FrameDestination>,
3042    src_ip: SpecifiedAddr<Ipv6Addr>,
3043    dst_ip: SpecifiedAddr<Ipv6Addr>,
3044    allow_dst_multicast: bool,
3045) -> bool {
3046    // NOTE: We do not explicitly implement the "unspecified address" check, as
3047    // it is enforced by the types of the arguments.
3048    let multicast_frame_dst = match frame_dst {
3049        Some(FrameDestination::Individual { local: _ }) | None => false,
3050        Some(FrameDestination::Broadcast) | Some(FrameDestination::Multicast) => true,
3051    };
3052    if (dst_ip.is_multicast() || multicast_frame_dst) && !allow_dst_multicast {
3053        return false;
3054    }
3055    if src_ip.is_loopback() || src_ip.is_multicast() {
3056        return false;
3057    }
3058    true
3059}
3060
3061/// Determine whether or not an IP packet body contains an ICMP error message
3062/// for the purposes of determining whether or not to send an ICMP response.
3063///
3064/// `is_icmp_error_message` checks whether `proto` is ICMP(v4) for IPv4 or
3065/// ICMPv6 for IPv6 and, if so, attempts to parse `buf` as an ICMP packet in
3066/// order to determine whether it is an error message or not. If parsing fails,
3067/// it conservatively assumes that it is an error packet in order to avoid
3068/// violating the MUST NOT directives of RFC 1122 Section 3.2.2 and [RFC 4443
3069/// Section 2.4.e].
3070///
3071/// [RFC 4443 Section 2.4.e]: https://tools.ietf.org/html/rfc4443#section-2.4
3072fn is_icmp_error_message<I: IcmpIpExt>(proto: I::Proto, buf: &[u8]) -> bool {
3073    proto == I::ICMP_IP_PROTO
3074        && peek_message_type::<I::IcmpMessageType>(buf).map(IcmpMessageType::is_err).unwrap_or(true)
3075}
3076
3077/// Test utilities for ICMP.
3078#[cfg(any(test, feature = "testutils"))]
3079pub(crate) mod testutil {
3080    use alloc::vec::Vec;
3081    use net_types::ethernet::Mac;
3082    use net_types::ip::{Ipv6, Ipv6Addr};
3083    use packet::{Buf, InnerPacketBuilder as _, Serializer as _};
3084    use packet_formats::icmp::ndp::options::NdpOptionBuilder;
3085    use packet_formats::icmp::ndp::{
3086        NeighborAdvertisement, NeighborSolicitation, OptionSequenceBuilder,
3087    };
3088    use packet_formats::icmp::{IcmpPacketBuilder, IcmpZeroCode};
3089    use packet_formats::ip::Ipv6Proto;
3090    use packet_formats::ipv6::Ipv6PacketBuilder;
3091
3092    use super::REQUIRED_NDP_IP_PACKET_HOP_LIMIT;
3093
3094    /// Serialize an IP packet containing a neighbor advertisement with the
3095    /// provided parameters.
3096    pub fn neighbor_advertisement_ip_packet(
3097        src_ip: Ipv6Addr,
3098        dst_ip: Ipv6Addr,
3099        router_flag: bool,
3100        solicited_flag: bool,
3101        override_flag: bool,
3102        mac: Mac,
3103    ) -> Buf<Vec<u8>> {
3104        OptionSequenceBuilder::new([NdpOptionBuilder::TargetLinkLayerAddress(&mac.bytes())].iter())
3105            .into_serializer()
3106            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
3107                src_ip,
3108                dst_ip,
3109                IcmpZeroCode,
3110                NeighborAdvertisement::new(router_flag, solicited_flag, override_flag, src_ip),
3111            ))
3112            .wrap_in(Ipv6PacketBuilder::new(
3113                src_ip,
3114                dst_ip,
3115                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
3116                Ipv6Proto::Icmpv6,
3117            ))
3118            .serialize_vec_outer()
3119            .unwrap()
3120            .unwrap_b()
3121    }
3122
3123    /// Serialize an IP packet containing a neighbor solicitation with the
3124    /// provided parameters.
3125    pub fn neighbor_solicitation_ip_packet(
3126        src_ip: Ipv6Addr,
3127        dst_ip: Ipv6Addr,
3128        target_addr: Ipv6Addr,
3129        mac: Mac,
3130    ) -> Buf<Vec<u8>> {
3131        OptionSequenceBuilder::new([NdpOptionBuilder::SourceLinkLayerAddress(&mac.bytes())].iter())
3132            .into_serializer()
3133            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
3134                src_ip,
3135                dst_ip,
3136                IcmpZeroCode,
3137                NeighborSolicitation::new(target_addr),
3138            ))
3139            .wrap_in(Ipv6PacketBuilder::new(
3140                src_ip,
3141                dst_ip,
3142                REQUIRED_NDP_IP_PACKET_HOP_LIMIT,
3143                Ipv6Proto::Icmpv6,
3144            ))
3145            .serialize_vec_outer()
3146            .unwrap()
3147            .unwrap_b()
3148    }
3149}
3150
3151#[cfg(test)]
3152mod tests {
3153    use alloc::vec;
3154    use alloc::vec::Vec;
3155    use packet_formats::icmp::ndp::options::NdpNonce;
3156
3157    use core::fmt::Debug;
3158    use core::time::Duration;
3159
3160    use net_types::ip::Subnet;
3161    use netstack3_base::testutil::{
3162        FakeBindingsCtx, FakeCoreCtx, FakeDeviceId, FakeInstant, FakeTxMetadata, FakeWeakDeviceId,
3163        TEST_ADDRS_V4, TEST_ADDRS_V6, TestIpExt, set_logger_for_test,
3164    };
3165    use netstack3_base::{CtxPair, Uninstantiable};
3166    use netstack3_filter::TransportPacketSerializer;
3167    use packet::{Buf, EmptyBuf};
3168    use packet_formats::icmp::mld::MldPacket;
3169    use packet_formats::ip::IpProto;
3170    use packet_formats::utils::NonZeroDuration;
3171
3172    use super::*;
3173    use crate::internal::base::{IpDeviceEgressStateContext, RouterAdvertisementEvent};
3174    use crate::internal::socket::testutil::{FakeDeviceConfig, FakeIpSocketCtx};
3175    use crate::internal::socket::{
3176        IpSock, IpSockCreationError, IpSockSendError, IpSocketHandler, SendOptions,
3177    };
3178    use crate::socket::RouteResolutionOptions;
3179
3180    /// The FakeCoreCtx held as the inner state of [`FakeIcmpCoreCtx`].
3181    type InnerIpSocketCtx<I> = FakeCoreCtx<
3182        FakeIpSocketCtx<I, FakeDeviceId>,
3183        SendIpPacketMeta<I, FakeDeviceId, SpecifiedAddr<<I as Ip>::Addr>>,
3184        FakeDeviceId,
3185    >;
3186
3187    /// `FakeCoreCtx` specialized for ICMP.
3188    pub(super) struct FakeIcmpCoreCtx<I: IpExt> {
3189        ip_socket_ctx: InnerIpSocketCtx<I>,
3190        icmp: FakeIcmpCoreCtxState<I>,
3191    }
3192
3193    /// `FakeBindingsCtx` specialized for ICMP.
3194    type FakeIcmpBindingsCtx<I> = FakeBindingsCtx<
3195        (),
3196        RouterAdvertisementEvent<FakeDeviceId>,
3197        FakeIcmpBindingsCtxState<I>,
3198        (),
3199    >;
3200
3201    /// A fake ICMP bindings and core contexts.
3202    ///
3203    /// This is exposed to super so it can be shared with the socket tests.
3204    pub(super) type FakeIcmpCtx<I> = CtxPair<FakeIcmpCoreCtx<I>, FakeIcmpBindingsCtx<I>>;
3205
3206    pub(super) struct FakeIcmpCoreCtxState<I: IpExt> {
3207        error_send_bucket: TokenBucket<FakeInstant>,
3208        receive_icmp_error: Vec<I::ErrorCode>,
3209        rx_counters: IcmpRxCounters<I>,
3210        tx_counters: IcmpTxCounters<I>,
3211        ndp_counters: NdpCounters,
3212    }
3213
3214    impl<I: TestIpExt + IpExt> FakeIcmpCoreCtx<I> {
3215        fn with_errors_per_second(errors_per_second: u64) -> Self {
3216            Self {
3217                icmp: FakeIcmpCoreCtxState {
3218                    error_send_bucket: TokenBucket::new(errors_per_second),
3219                    receive_icmp_error: Default::default(),
3220                    rx_counters: Default::default(),
3221                    tx_counters: Default::default(),
3222                    ndp_counters: Default::default(),
3223                },
3224                ip_socket_ctx: InnerIpSocketCtx::with_state(FakeIpSocketCtx::new(
3225                    core::iter::once(FakeDeviceConfig {
3226                        device: FakeDeviceId,
3227                        local_ips: vec![I::TEST_ADDRS.local_ip],
3228                        remote_ips: vec![I::TEST_ADDRS.remote_ip],
3229                    }),
3230                )),
3231            }
3232        }
3233    }
3234
3235    impl<I: TestIpExt + IpExt> Default for FakeIcmpCoreCtx<I> {
3236        fn default() -> Self {
3237            Self::with_errors_per_second(DEFAULT_ERRORS_PER_SECOND)
3238        }
3239    }
3240
3241    impl<I: IpExt> DeviceIdContext<AnyDevice> for FakeIcmpCoreCtx<I> {
3242        type DeviceId = FakeDeviceId;
3243        type WeakDeviceId = FakeWeakDeviceId<FakeDeviceId>;
3244    }
3245
3246    impl<I: IpExt> IcmpStateContext for FakeIcmpCoreCtx<I> {}
3247    impl<I: IpExt> IcmpStateContext for InnerIpSocketCtx<I> {}
3248
3249    impl<I: IpExt> CounterContext<IcmpRxCounters<I>> for FakeIcmpCoreCtx<I> {
3250        fn counters(&self) -> &IcmpRxCounters<I> {
3251            &self.icmp.rx_counters
3252        }
3253    }
3254
3255    impl<I: IpExt> CounterContext<IcmpTxCounters<I>> for FakeIcmpCoreCtx<I> {
3256        fn counters(&self) -> &IcmpTxCounters<I> {
3257            &self.icmp.tx_counters
3258        }
3259    }
3260
3261    impl<I: IpExt> CounterContext<NdpCounters> for FakeIcmpCoreCtx<I> {
3262        fn counters(&self) -> &NdpCounters {
3263            &self.icmp.ndp_counters
3264        }
3265    }
3266
3267    pub enum FakeEchoIpTransportContext {}
3268
3269    impl EchoTransportContextMarker for FakeEchoIpTransportContext {}
3270
3271    impl<I: IpExt> IpTransportContext<I, FakeIcmpBindingsCtx<I>, FakeIcmpCoreCtx<I>>
3272        for FakeEchoIpTransportContext
3273    {
3274        fn receive_icmp_error(
3275            core_ctx: &mut FakeIcmpCoreCtx<I>,
3276            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3277            _device: &FakeDeviceId,
3278            _original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3279            _original_dst_ip: SpecifiedAddr<I::Addr>,
3280            _original_body: &[u8],
3281            _err: I::ErrorCode,
3282        ) {
3283            core_ctx.icmp.rx_counters.error_delivered_to_socket.increment()
3284        }
3285
3286        fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
3287            _core_ctx: &mut FakeIcmpCoreCtx<I>,
3288            _bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3289            _device: &FakeDeviceId,
3290            _src_ip: I::RecvSrcAddr,
3291            _dst_ip: SpecifiedAddr<I::Addr>,
3292            _buffer: B,
3293            _info: &LocalDeliveryPacketInfo<I, H>,
3294        ) -> Result<(), (B, TransportReceiveError)> {
3295            unimplemented!()
3296        }
3297    }
3298
3299    impl<I: IpExt + FilterIpExt> InnerIcmpContext<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3300        type EchoTransportContext = FakeEchoIpTransportContext;
3301
3302        fn receive_icmp_error(
3303            &mut self,
3304            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3305            device: &Self::DeviceId,
3306            original_src_ip: Option<SpecifiedAddr<I::Addr>>,
3307            original_dst_ip: SpecifiedAddr<I::Addr>,
3308            original_proto: I::Proto,
3309            original_body: &[u8],
3310            err: I::ErrorCode,
3311        ) {
3312            CounterContext::<IcmpRxCounters<I>>::counters(self).error.increment();
3313            self.icmp.receive_icmp_error.push(err);
3314            if original_proto == I::ICMP_IP_PROTO {
3315                receive_ip_transport_icmp_error(
3316                    self,
3317                    bindings_ctx,
3318                    device,
3319                    original_src_ip,
3320                    original_dst_ip,
3321                    original_body,
3322                    err,
3323                )
3324            }
3325        }
3326
3327        fn with_error_send_bucket_mut<O, F: FnOnce(&mut TokenBucket<FakeInstant>) -> O>(
3328            &mut self,
3329            cb: F,
3330        ) -> O {
3331            cb(&mut self.icmp.error_send_bucket)
3332        }
3333    }
3334
3335    #[test]
3336    fn test_should_send_icmpv4_error() {
3337        let src_ip = TEST_ADDRS_V4.local_ip;
3338        let dst_ip = TEST_ADDRS_V4.remote_ip;
3339        let frame_dst = FrameDestination::Individual { local: true };
3340        let multicast_ip_1 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 1])).unwrap();
3341        let multicast_ip_2 = SpecifiedAddr::new(Ipv4Addr::new([224, 0, 0, 2])).unwrap();
3342
3343        // Should Send, unless non initial fragment.
3344        assert!(should_send_icmpv4_error(
3345            Some(frame_dst),
3346            src_ip,
3347            dst_ip,
3348            Ipv4FragmentType::InitialFragment
3349        ));
3350        assert!(should_send_icmpv4_error(None, src_ip, dst_ip, Ipv4FragmentType::InitialFragment));
3351        assert!(!should_send_icmpv4_error(
3352            Some(frame_dst),
3353            src_ip,
3354            dst_ip,
3355            Ipv4FragmentType::NonInitialFragment
3356        ));
3357
3358        // Should not send because destined for IP broadcast addr
3359        assert!(!should_send_icmpv4_error(
3360            Some(frame_dst),
3361            src_ip,
3362            Ipv4::LIMITED_BROADCAST_ADDRESS,
3363            Ipv4FragmentType::InitialFragment
3364        ));
3365        assert!(!should_send_icmpv4_error(
3366            Some(frame_dst),
3367            src_ip,
3368            Ipv4::LIMITED_BROADCAST_ADDRESS,
3369            Ipv4FragmentType::NonInitialFragment
3370        ));
3371
3372        // Should not send because destined for multicast addr
3373        assert!(!should_send_icmpv4_error(
3374            Some(frame_dst),
3375            src_ip,
3376            multicast_ip_1,
3377            Ipv4FragmentType::InitialFragment
3378        ));
3379        assert!(!should_send_icmpv4_error(
3380            Some(frame_dst),
3381            src_ip,
3382            multicast_ip_1,
3383            Ipv4FragmentType::NonInitialFragment
3384        ));
3385
3386        // Should not send because Link Layer Broadcast.
3387        assert!(!should_send_icmpv4_error(
3388            Some(FrameDestination::Broadcast),
3389            src_ip,
3390            dst_ip,
3391            Ipv4FragmentType::InitialFragment
3392        ));
3393        assert!(!should_send_icmpv4_error(
3394            Some(FrameDestination::Broadcast),
3395            src_ip,
3396            dst_ip,
3397            Ipv4FragmentType::NonInitialFragment
3398        ));
3399
3400        // Should not send because from loopback addr
3401        assert!(!should_send_icmpv4_error(
3402            Some(frame_dst),
3403            Ipv4::LOOPBACK_ADDRESS,
3404            dst_ip,
3405            Ipv4FragmentType::InitialFragment
3406        ));
3407        assert!(!should_send_icmpv4_error(
3408            Some(frame_dst),
3409            Ipv4::LOOPBACK_ADDRESS,
3410            dst_ip,
3411            Ipv4FragmentType::NonInitialFragment
3412        ));
3413
3414        // Should not send because from limited broadcast addr
3415        assert!(!should_send_icmpv4_error(
3416            Some(frame_dst),
3417            Ipv4::LIMITED_BROADCAST_ADDRESS,
3418            dst_ip,
3419            Ipv4FragmentType::InitialFragment
3420        ));
3421        assert!(!should_send_icmpv4_error(
3422            Some(frame_dst),
3423            Ipv4::LIMITED_BROADCAST_ADDRESS,
3424            dst_ip,
3425            Ipv4FragmentType::NonInitialFragment
3426        ));
3427
3428        // Should not send because from multicast addr
3429        assert!(!should_send_icmpv4_error(
3430            Some(frame_dst),
3431            multicast_ip_2,
3432            dst_ip,
3433            Ipv4FragmentType::InitialFragment
3434        ));
3435        assert!(!should_send_icmpv4_error(
3436            Some(frame_dst),
3437            multicast_ip_2,
3438            dst_ip,
3439            Ipv4FragmentType::NonInitialFragment
3440        ));
3441
3442        // Should not send because from class E addr
3443        assert!(!should_send_icmpv4_error(
3444            Some(frame_dst),
3445            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
3446            dst_ip,
3447            Ipv4FragmentType::InitialFragment
3448        ));
3449        assert!(!should_send_icmpv4_error(
3450            Some(frame_dst),
3451            SpecifiedAddr::new(Ipv4Addr::new([240, 0, 0, 1])).unwrap(),
3452            dst_ip,
3453            Ipv4FragmentType::NonInitialFragment
3454        ));
3455    }
3456
3457    #[test]
3458    fn test_should_send_icmpv6_error() {
3459        let src_ip = TEST_ADDRS_V6.local_ip;
3460        let dst_ip = TEST_ADDRS_V6.remote_ip;
3461        let frame_dst = FrameDestination::Individual { local: true };
3462        let multicast_ip_1 =
3463            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 1])).unwrap();
3464        let multicast_ip_2 =
3465            SpecifiedAddr::new(Ipv6Addr::new([0xff00, 0, 0, 0, 0, 0, 0, 2])).unwrap();
3466
3467        // Should Send.
3468        assert!(should_send_icmpv6_error(
3469            Some(frame_dst),
3470            src_ip,
3471            dst_ip,
3472            false /* allow_dst_multicast */
3473        ));
3474        assert!(should_send_icmpv6_error(
3475            None, src_ip, dst_ip, false /* allow_dst_multicast */
3476        ));
3477        assert!(should_send_icmpv6_error(
3478            Some(frame_dst),
3479            src_ip,
3480            dst_ip,
3481            true /* allow_dst_multicast */
3482        ));
3483
3484        // Should not send because destined for multicast addr, unless exception
3485        // applies.
3486        assert!(!should_send_icmpv6_error(
3487            Some(frame_dst),
3488            src_ip,
3489            multicast_ip_1,
3490            false /* allow_dst_multicast */
3491        ));
3492        assert!(should_send_icmpv6_error(
3493            Some(frame_dst),
3494            src_ip,
3495            multicast_ip_1,
3496            true /* allow_dst_multicast */
3497        ));
3498
3499        // Should not send because Link Layer Broadcast, unless exception
3500        // applies.
3501        assert!(!should_send_icmpv6_error(
3502            Some(FrameDestination::Broadcast),
3503            src_ip,
3504            dst_ip,
3505            false /* allow_dst_multicast */
3506        ));
3507        assert!(should_send_icmpv6_error(
3508            Some(FrameDestination::Broadcast),
3509            src_ip,
3510            dst_ip,
3511            true /* allow_dst_multicast */
3512        ));
3513
3514        // Should not send because from loopback addr.
3515        assert!(!should_send_icmpv6_error(
3516            Some(frame_dst),
3517            Ipv6::LOOPBACK_ADDRESS,
3518            dst_ip,
3519            false /* allow_dst_multicast */
3520        ));
3521        assert!(!should_send_icmpv6_error(
3522            Some(frame_dst),
3523            Ipv6::LOOPBACK_ADDRESS,
3524            dst_ip,
3525            true /* allow_dst_multicast */
3526        ));
3527
3528        // Should not send because from multicast addr.
3529        assert!(!should_send_icmpv6_error(
3530            Some(frame_dst),
3531            multicast_ip_2,
3532            dst_ip,
3533            false /* allow_dst_multicast */
3534        ));
3535        assert!(!should_send_icmpv6_error(
3536            Some(frame_dst),
3537            multicast_ip_2,
3538            dst_ip,
3539            true /* allow_dst_multicast */
3540        ));
3541
3542        // Should not send because from multicast addr, even though dest
3543        // multicast exception applies.
3544        assert!(!should_send_icmpv6_error(
3545            Some(FrameDestination::Broadcast),
3546            multicast_ip_2,
3547            dst_ip,
3548            false /* allow_dst_multicast */
3549        ));
3550        assert!(!should_send_icmpv6_error(
3551            Some(FrameDestination::Broadcast),
3552            multicast_ip_2,
3553            dst_ip,
3554            true /* allow_dst_multicast */
3555        ));
3556        assert!(!should_send_icmpv6_error(
3557            Some(frame_dst),
3558            multicast_ip_2,
3559            multicast_ip_1,
3560            false /* allow_dst_multicast */
3561        ));
3562        assert!(!should_send_icmpv6_error(
3563            Some(frame_dst),
3564            multicast_ip_2,
3565            multicast_ip_1,
3566            true /* allow_dst_multicast */
3567        ));
3568    }
3569
3570    // Tests that only require an ICMP stack. Unlike the preceding tests, these
3571    // only test the ICMP stack and state, and fake everything else. We define
3572    // the `FakeIcmpv4Ctx` and `FakeIcmpv6Ctx` types, which we wrap in a
3573    // `FakeCtx` to provide automatic implementations of a number of required
3574    // traits. The rest we implement manually.
3575
3576    #[derive(Default)]
3577    pub(super) struct FakeIcmpBindingsCtxState<I: IpExt> {
3578        _marker: core::marker::PhantomData<I>,
3579    }
3580
3581    impl InnerIcmpv4Context<FakeIcmpBindingsCtx<Ipv4>> for FakeIcmpCoreCtx<Ipv4> {
3582        fn should_send_timestamp_reply(&self) -> bool {
3583            false
3584        }
3585    }
3586    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv4>, FakeIcmpBindingsCtx<Ipv4>, Ipv4);
3587    impl_pmtu_handler!(FakeIcmpCoreCtx<Ipv6>, FakeIcmpBindingsCtx<Ipv6>, Ipv6);
3588
3589    impl<I: IpExt + FilterIpExt> IpSocketHandler<I, FakeIcmpBindingsCtx<I>> for FakeIcmpCoreCtx<I> {
3590        fn new_ip_socket<O>(
3591            &mut self,
3592            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3593            args: IpSocketArgs<'_, Self::DeviceId, I, O>,
3594        ) -> Result<IpSock<I, Self::WeakDeviceId>, IpSockCreationError>
3595        where
3596            O: RouteResolutionOptions<I>,
3597        {
3598            self.ip_socket_ctx.new_ip_socket(bindings_ctx, args)
3599        }
3600
3601        fn send_ip_packet<S, O>(
3602            &mut self,
3603            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3604            socket: &IpSock<I, Self::WeakDeviceId>,
3605            body: S,
3606            options: &O,
3607            tx_meta: FakeTxMetadata,
3608        ) -> Result<(), IpSockSendError>
3609        where
3610            S: TransportPacketSerializer<I>,
3611            S::Buffer: BufferMut,
3612            O: SendOptions<I> + RouteResolutionOptions<I>,
3613        {
3614            self.ip_socket_ctx.send_ip_packet(bindings_ctx, socket, body, options, tx_meta)
3615        }
3616
3617        fn confirm_reachable<O>(
3618            &mut self,
3619            bindings_ctx: &mut FakeIcmpBindingsCtx<I>,
3620            socket: &IpSock<I, Self::WeakDeviceId>,
3621            options: &O,
3622        ) where
3623            O: RouteResolutionOptions<I>,
3624        {
3625            self.ip_socket_ctx.confirm_reachable(bindings_ctx, socket, options)
3626        }
3627    }
3628
3629    impl IpDeviceHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3630        fn is_router_device(&mut self, _device_id: &Self::DeviceId) -> bool {
3631            unimplemented!()
3632        }
3633
3634        fn set_default_hop_limit(&mut self, _device_id: &Self::DeviceId, _hop_limit: NonZeroU8) {
3635            unreachable!()
3636        }
3637
3638        fn handle_received_dad_packet(
3639            &mut self,
3640            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3641            _device_id: &Self::DeviceId,
3642            _addr: SpecifiedAddr<Ipv6Addr>,
3643            _probe_data: Option<NdpNonce<&'_ [u8]>>,
3644        ) -> IpAddressState {
3645            unimplemented!()
3646        }
3647    }
3648
3649    impl IpDeviceEgressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3650        fn with_next_packet_id<O, F: FnOnce(&()) -> O>(&self, cb: F) -> O {
3651            cb(&())
3652        }
3653
3654        fn get_local_addr_for_remote(
3655            &mut self,
3656            _device_id: &Self::DeviceId,
3657            _remote: Option<SpecifiedAddr<Ipv6Addr>>,
3658        ) -> Option<IpDeviceAddr<Ipv6Addr>> {
3659            unimplemented!()
3660        }
3661
3662        fn get_hop_limit(&mut self, _device_id: &Self::DeviceId) -> NonZeroU8 {
3663            unimplemented!()
3664        }
3665    }
3666
3667    impl IpDeviceIngressStateContext<Ipv6> for FakeIcmpCoreCtx<Ipv6> {
3668        fn address_status_for_device(
3669            &mut self,
3670            _addr: SpecifiedAddr<Ipv6Addr>,
3671            _device_id: &Self::DeviceId,
3672        ) -> AddressStatus<Ipv6PresentAddressStatus> {
3673            unimplemented!()
3674        }
3675    }
3676
3677    impl Ipv6DeviceHandler<FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3678        type LinkLayerAddr = Uninstantiable;
3679
3680        fn get_link_layer_addr(&mut self, _device_id: &Self::DeviceId) -> Option<Uninstantiable> {
3681            unimplemented!()
3682        }
3683
3684        fn set_discovered_retrans_timer(
3685            &mut self,
3686            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3687            _device_id: &Self::DeviceId,
3688            _retrans_timer: NonZeroDuration,
3689        ) {
3690            unimplemented!()
3691        }
3692
3693        fn set_link_mtu(&mut self, _device_id: &Self::DeviceId, _mtu: Mtu) {
3694            unimplemented!()
3695        }
3696
3697        fn update_discovered_ipv6_route(
3698            &mut self,
3699            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3700            _device_id: &Self::DeviceId,
3701            _route: Ipv6DiscoveredRoute,
3702            _lifetime: Option<NonZeroNdpLifetime>,
3703        ) {
3704            unimplemented!()
3705        }
3706
3707        fn apply_slaac_update(
3708            &mut self,
3709            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3710            _device_id: &Self::DeviceId,
3711            _subnet: Subnet<Ipv6Addr>,
3712            _preferred_lifetime: Option<NonZeroNdpLifetime>,
3713            _valid_lifetime: Option<NonZeroNdpLifetime>,
3714        ) {
3715            unimplemented!()
3716        }
3717
3718        fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
3719            &mut self,
3720            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3721            _device: &FakeDeviceId,
3722            _src_ip: Ipv6SourceAddr,
3723            _dst_ip: SpecifiedAddr<Ipv6Addr>,
3724            _packet: MldPacket<B>,
3725            _header_info: &H,
3726        ) {
3727            unimplemented!()
3728        }
3729    }
3730
3731    impl IpLayerHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3732        fn send_ip_packet_from_device<S>(
3733            &mut self,
3734            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3735            _meta: SendIpPacketMeta<Ipv6, &Self::DeviceId, Option<SpecifiedAddr<Ipv6Addr>>>,
3736            _body: S,
3737        ) -> Result<(), IpSendFrameError<S>> {
3738            unimplemented!()
3739        }
3740
3741        fn send_ip_frame<S>(
3742            &mut self,
3743            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3744            _device: &Self::DeviceId,
3745            _destination: IpPacketDestination<Ipv6, &Self::DeviceId>,
3746            _body: S,
3747        ) -> Result<(), IpSendFrameError<S>>
3748        where
3749            S: Serializer,
3750            S::Buffer: BufferMut,
3751        {
3752            unimplemented!()
3753        }
3754    }
3755
3756    impl NudIpHandler<Ipv6, FakeIcmpBindingsCtx<Ipv6>> for FakeIcmpCoreCtx<Ipv6> {
3757        fn handle_neighbor_probe(
3758            &mut self,
3759            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3760            _device_id: &Self::DeviceId,
3761            _neighbor: SpecifiedAddr<Ipv6Addr>,
3762            _link_addr: &[u8],
3763        ) {
3764            unimplemented!()
3765        }
3766
3767        fn handle_neighbor_confirmation(
3768            &mut self,
3769            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3770            _device_id: &Self::DeviceId,
3771            _neighbor: SpecifiedAddr<Ipv6Addr>,
3772            _link_addr: Option<&[u8]>,
3773            _flags: ConfirmationFlags,
3774        ) {
3775            unimplemented!()
3776        }
3777
3778        fn flush_neighbor_table(
3779            &mut self,
3780            _bindings_ctx: &mut FakeIcmpBindingsCtx<Ipv6>,
3781            _device_id: &Self::DeviceId,
3782        ) {
3783            unimplemented!()
3784        }
3785    }
3786
3787    #[test]
3788    fn test_receive_icmpv4_error() {
3789        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
3790        // the value 0) and, b) different from each other.
3791        const ICMP_ID: u16 = 0x0F;
3792        const SEQ_NUM: u16 = 0xF0;
3793
3794        /// Test receiving an ICMP error message.
3795        ///
3796        /// Test that receiving an ICMP error message with the given code and
3797        /// message contents, and containing the given original IPv4 packet,
3798        /// results in the counter values in `assert_counters`. After that
3799        /// assertion passes, `f` is called on the context so that the caller
3800        /// can perform whatever extra validation they want.
3801        ///
3802        /// The error message will be sent from `TEST_ADDRS_V4.remote_ip` to
3803        /// `TEST_ADDRS_V4.local_ip`. Before the message is sent, an ICMP
3804        /// socket will be established with the ID `ICMP_ID`, and
3805        /// `test_receive_icmpv4_error_helper` will assert that its `SocketId`
3806        /// is 0. This allows the caller to craft the `original_packet` so that
3807        /// it should be delivered to this socket.
3808        fn test_receive_icmpv4_error_helper<
3809            C: Debug,
3810            M: IcmpMessage<Ipv4, Code = C> + Debug,
3811            F: Fn(&FakeIcmpCtx<Ipv4>),
3812        >(
3813            original_packet: &mut [u8],
3814            code: C,
3815            msg: M,
3816            f: F,
3817        ) {
3818            set_logger_for_test();
3819
3820            let mut ctx: FakeIcmpCtx<Ipv4> = FakeIcmpCtx::default();
3821
3822            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
3823            <IcmpIpTransportContext as IpTransportContext<Ipv4, _, _>>::receive_ip_packet(
3824                core_ctx,
3825                bindings_ctx,
3826                &FakeDeviceId,
3827                Ipv4SourceAddr::new(*TEST_ADDRS_V4.remote_ip).unwrap(),
3828                TEST_ADDRS_V4.local_ip,
3829                IcmpPacketBuilder::new(TEST_ADDRS_V4.remote_ip, TEST_ADDRS_V4.local_ip, code, msg)
3830                    .wrap_body(Buf::new(original_packet, ..))
3831                    .serialize_vec_outer()
3832                    .unwrap(),
3833                &LocalDeliveryPacketInfo::default(),
3834            )
3835            .unwrap();
3836            f(&ctx);
3837        }
3838        // Test that, when we receive various ICMPv4 error messages, we properly
3839        // pass them up to the IP layer and, sometimes, to the transport layer.
3840
3841        // First, test with an original packet containing an ICMP message. Since
3842        // this test fake supports ICMP sockets, this error can be delivered all
3843        // the way up the stack.
3844
3845        // A buffer containing an ICMP echo request with ID `ICMP_ID` and
3846        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
3847        // ICMP error message which contains this as its original packet should
3848        // be delivered to the socket created in
3849        // `test_receive_icmpv4_error_helper`.
3850        let mut buffer = EmptyBuf
3851            .wrap_in(IcmpPacketBuilder::<Ipv4, _>::new(
3852                TEST_ADDRS_V4.local_ip,
3853                TEST_ADDRS_V4.remote_ip,
3854                IcmpZeroCode,
3855                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
3856            ))
3857            .wrap_in(<Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3858                TEST_ADDRS_V4.local_ip,
3859                TEST_ADDRS_V4.remote_ip,
3860                64,
3861                Ipv4Proto::Icmp,
3862            ))
3863            .serialize_vec_outer()
3864            .unwrap();
3865
3866        test_receive_icmpv4_error_helper(
3867            buffer.as_mut(),
3868            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3869            IcmpDestUnreachable::default(),
3870            |CtxPair { core_ctx, bindings_ctx: _ }| {
3871                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3872                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3873                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3874                let err = Icmpv4ErrorCode::DestUnreachable(
3875                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3876                    IcmpDestUnreachable::default(),
3877                );
3878                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3879            },
3880        );
3881
3882        test_receive_icmpv4_error_helper(
3883            buffer.as_mut(),
3884            Icmpv4TimeExceededCode::TtlExpired,
3885            IcmpTimeExceeded::default(),
3886            |CtxPair { core_ctx, bindings_ctx: _ }| {
3887                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3888                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3889                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3890                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3891                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3892            },
3893        );
3894
3895        test_receive_icmpv4_error_helper(
3896            buffer.as_mut(),
3897            Icmpv4ParameterProblemCode::PointerIndicatesError,
3898            Icmpv4ParameterProblem::new(0),
3899            |CtxPair { core_ctx, bindings_ctx: _ }| {
3900                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3901                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3902                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
3903                let err = Icmpv4ErrorCode::ParameterProblem(
3904                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3905                );
3906                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3907            },
3908        );
3909
3910        // Second, test with an original packet containing a malformed ICMP
3911        // packet (we accomplish this by leaving the IP packet's body empty). We
3912        // should process this packet in
3913        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
3914        // further - in particular, we should not dispatch to the Echo sockets.
3915
3916        let mut buffer = <Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3917            TEST_ADDRS_V4.local_ip,
3918            TEST_ADDRS_V4.remote_ip,
3919            64,
3920            Ipv4Proto::Icmp,
3921        )
3922        .wrap_body(EmptyBuf)
3923        .serialize_vec_outer()
3924        .unwrap();
3925
3926        test_receive_icmpv4_error_helper(
3927            buffer.as_mut(),
3928            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3929            IcmpDestUnreachable::default(),
3930            |CtxPair { core_ctx, bindings_ctx: _ }| {
3931                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3932                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3933                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3934                let err = Icmpv4ErrorCode::DestUnreachable(
3935                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3936                    IcmpDestUnreachable::default(),
3937                );
3938                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3939            },
3940        );
3941
3942        test_receive_icmpv4_error_helper(
3943            buffer.as_mut(),
3944            Icmpv4TimeExceededCode::TtlExpired,
3945            IcmpTimeExceeded::default(),
3946            |CtxPair { core_ctx, bindings_ctx: _ }| {
3947                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3948                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3949                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3950                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
3951                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3952            },
3953        );
3954
3955        test_receive_icmpv4_error_helper(
3956            buffer.as_mut(),
3957            Icmpv4ParameterProblemCode::PointerIndicatesError,
3958            Icmpv4ParameterProblem::new(0),
3959            |CtxPair { core_ctx, bindings_ctx: _ }| {
3960                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3961                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
3962                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3963                let err = Icmpv4ErrorCode::ParameterProblem(
3964                    Icmpv4ParameterProblemCode::PointerIndicatesError,
3965                );
3966                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3967            },
3968        );
3969
3970        // Third, test with an original packet containing a UDP packet. This
3971        // allows us to verify that protocol numbers are handled properly by
3972        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
3973        // called.
3974
3975        let mut buffer = <Ipv4 as packet_formats::ip::IpExt>::PacketBuilder::new(
3976            TEST_ADDRS_V4.local_ip,
3977            TEST_ADDRS_V4.remote_ip,
3978            64,
3979            IpProto::Udp.into(),
3980        )
3981        .wrap_body(EmptyBuf)
3982        .serialize_vec_outer()
3983        .unwrap();
3984
3985        test_receive_icmpv4_error_helper(
3986            buffer.as_mut(),
3987            Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3988            IcmpDestUnreachable::default(),
3989            |CtxPair { core_ctx, bindings_ctx: _ }| {
3990                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
3991                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
3992                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
3993                let err = Icmpv4ErrorCode::DestUnreachable(
3994                    Icmpv4DestUnreachableCode::DestNetworkUnreachable,
3995                    IcmpDestUnreachable::default(),
3996                );
3997                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
3998            },
3999        );
4000
4001        test_receive_icmpv4_error_helper(
4002            buffer.as_mut(),
4003            Icmpv4TimeExceededCode::TtlExpired,
4004            IcmpTimeExceeded::default(),
4005            |CtxPair { core_ctx, bindings_ctx: _ }| {
4006                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4007                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4008                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4009                let err = Icmpv4ErrorCode::TimeExceeded(Icmpv4TimeExceededCode::TtlExpired);
4010                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4011            },
4012        );
4013
4014        test_receive_icmpv4_error_helper(
4015            buffer.as_mut(),
4016            Icmpv4ParameterProblemCode::PointerIndicatesError,
4017            Icmpv4ParameterProblem::new(0),
4018            |CtxPair { core_ctx, bindings_ctx: _ }| {
4019                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4020                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4021                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4022                let err = Icmpv4ErrorCode::ParameterProblem(
4023                    Icmpv4ParameterProblemCode::PointerIndicatesError,
4024                );
4025                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4026            },
4027        );
4028    }
4029
4030    #[test]
4031    fn test_receive_icmpv6_error() {
4032        // Chosen arbitrarily to be a) non-zero (it's easy to accidentally get
4033        // the value 0) and, b) different from each other.
4034        const ICMP_ID: u16 = 0x0F;
4035        const SEQ_NUM: u16 = 0xF0;
4036
4037        /// Test receiving an ICMPv6 error message.
4038        ///
4039        /// Test that receiving an ICMP error message with the given code and
4040        /// message contents, and containing the given original IPv4 packet,
4041        /// results in the counter values in `assert_counters`. After that
4042        /// assertion passes, `f` is called on the context so that the caller
4043        /// can perform whatever extra validation they want.
4044        ///
4045        /// The error message will be sent from `TEST_ADDRS_V6.remote_ip` to
4046        /// `TEST_ADDRS_V6.local_ip`. Before the message is sent, an ICMP
4047        /// socket will be established with the ID `ICMP_ID`, and
4048        /// `test_receive_icmpv6_error_helper` will assert that its `SocketId`
4049        /// is 0. This allows the caller to craft the `original_packet` so that
4050        /// it should be delivered to this socket.
4051        fn test_receive_icmpv6_error_helper<
4052            C: Debug,
4053            M: IcmpMessage<Ipv6, Code = C> + Debug,
4054            F: Fn(&FakeIcmpCtx<Ipv6>),
4055        >(
4056            original_packet: &mut [u8],
4057            code: C,
4058            msg: M,
4059            f: F,
4060        ) {
4061            set_logger_for_test();
4062
4063            let mut ctx = FakeIcmpCtx::<Ipv6>::default();
4064            let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
4065            <IcmpIpTransportContext as IpTransportContext<Ipv6, _, _>>::receive_ip_packet(
4066                core_ctx,
4067                bindings_ctx,
4068                &FakeDeviceId,
4069                TEST_ADDRS_V6.remote_ip.get().try_into().unwrap(),
4070                TEST_ADDRS_V6.local_ip,
4071                IcmpPacketBuilder::new(TEST_ADDRS_V6.remote_ip, TEST_ADDRS_V6.local_ip, code, msg)
4072                    .wrap_body(Buf::new(original_packet, ..))
4073                    .serialize_vec_outer()
4074                    .unwrap(),
4075                &LocalDeliveryPacketInfo::default(),
4076            )
4077            .unwrap();
4078            f(&ctx);
4079        }
4080        // Test that, when we receive various ICMPv6 error messages, we properly
4081        // pass them up to the IP layer and, sometimes, to the transport layer.
4082
4083        // First, test with an original packet containing an ICMPv6 message.
4084        // Since this test fake supports ICMPv6 sockets, this error can be
4085        // delivered all the way up the stack.
4086
4087        // A buffer containing an ICMPv6 echo request with ID `ICMP_ID` and
4088        // sequence number `SEQ_NUM` from the local IP to the remote IP. Any
4089        // ICMPv6 error message which contains this as its original packet
4090        // should be delivered to the socket created in
4091        // `test_receive_icmpv6_error_helper`.
4092        let mut buffer = EmptyBuf
4093            .wrap_in(IcmpPacketBuilder::<Ipv6, _>::new(
4094                TEST_ADDRS_V6.local_ip,
4095                TEST_ADDRS_V6.remote_ip,
4096                IcmpZeroCode,
4097                IcmpEchoRequest::new(ICMP_ID, SEQ_NUM),
4098            ))
4099            .wrap_in(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4100                TEST_ADDRS_V6.local_ip,
4101                TEST_ADDRS_V6.remote_ip,
4102                64,
4103                Ipv6Proto::Icmpv6,
4104            ))
4105            .serialize_vec_outer()
4106            .unwrap();
4107
4108        test_receive_icmpv6_error_helper(
4109            buffer.as_mut(),
4110            Icmpv6DestUnreachableCode::NoRoute,
4111            IcmpDestUnreachable::default(),
4112            |CtxPair { core_ctx, bindings_ctx: _ }| {
4113                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4114                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4115                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4116                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4117                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4118            },
4119        );
4120
4121        test_receive_icmpv6_error_helper(
4122            buffer.as_mut(),
4123            Icmpv6TimeExceededCode::HopLimitExceeded,
4124            IcmpTimeExceeded::default(),
4125            |CtxPair { core_ctx, bindings_ctx: _ }| {
4126                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4127                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4128                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4129                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4130                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4131            },
4132        );
4133
4134        test_receive_icmpv6_error_helper(
4135            buffer.as_mut(),
4136            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4137            Icmpv6ParameterProblem::new(0),
4138            |CtxPair { core_ctx, bindings_ctx: _ }| {
4139                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4140                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4141                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 1);
4142                let err = Icmpv6ErrorCode::ParameterProblem(
4143                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4144                );
4145                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4146            },
4147        );
4148
4149        // Second, test with an original packet containing a malformed ICMPv6
4150        // packet (we accomplish this by leaving the IP packet's body empty). We
4151        // should process this packet in
4152        // `IcmpIpTransportContext::receive_icmp_error`, but we should go no
4153        // further - in particular, we should not call into Echo sockets.
4154
4155        let mut buffer = EmptyBuf
4156            .wrap_in(<Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4157                TEST_ADDRS_V6.local_ip,
4158                TEST_ADDRS_V6.remote_ip,
4159                64,
4160                Ipv6Proto::Icmpv6,
4161            ))
4162            .serialize_vec_outer()
4163            .unwrap();
4164
4165        test_receive_icmpv6_error_helper(
4166            buffer.as_mut(),
4167            Icmpv6DestUnreachableCode::NoRoute,
4168            IcmpDestUnreachable::default(),
4169            |CtxPair { core_ctx, bindings_ctx: _ }| {
4170                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4171                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4172                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4173                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4174                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4175            },
4176        );
4177
4178        test_receive_icmpv6_error_helper(
4179            buffer.as_mut(),
4180            Icmpv6TimeExceededCode::HopLimitExceeded,
4181            IcmpTimeExceeded::default(),
4182            |CtxPair { core_ctx, bindings_ctx: _ }| {
4183                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4184                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4185                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4186                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4187                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4188            },
4189        );
4190
4191        test_receive_icmpv6_error_helper(
4192            buffer.as_mut(),
4193            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4194            Icmpv6ParameterProblem::new(0),
4195            |CtxPair { core_ctx, bindings_ctx: _ }| {
4196                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4197                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 1);
4198                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4199                let err = Icmpv6ErrorCode::ParameterProblem(
4200                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4201                );
4202                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4203            },
4204        );
4205
4206        // Third, test with an original packet containing a UDP packet. This
4207        // allows us to verify that protocol numbers are handled properly by
4208        // checking that `IcmpIpTransportContext::receive_icmp_error` was NOT
4209        // called.
4210
4211        let mut buffer = <Ipv6 as packet_formats::ip::IpExt>::PacketBuilder::new(
4212            TEST_ADDRS_V6.local_ip,
4213            TEST_ADDRS_V6.remote_ip,
4214            64,
4215            IpProto::Udp.into(),
4216        )
4217        .wrap_body(EmptyBuf)
4218        .serialize_vec_outer()
4219        .unwrap();
4220
4221        test_receive_icmpv6_error_helper(
4222            buffer.as_mut(),
4223            Icmpv6DestUnreachableCode::NoRoute,
4224            IcmpDestUnreachable::default(),
4225            |CtxPair { core_ctx, bindings_ctx: _ }| {
4226                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4227                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4228                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4229                let err = Icmpv6ErrorCode::DestUnreachable(Icmpv6DestUnreachableCode::NoRoute);
4230                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4231            },
4232        );
4233
4234        test_receive_icmpv6_error_helper(
4235            buffer.as_mut(),
4236            Icmpv6TimeExceededCode::HopLimitExceeded,
4237            IcmpTimeExceeded::default(),
4238            |CtxPair { core_ctx, bindings_ctx: _ }| {
4239                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4240                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4241                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4242                let err = Icmpv6ErrorCode::TimeExceeded(Icmpv6TimeExceededCode::HopLimitExceeded);
4243                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4244            },
4245        );
4246
4247        test_receive_icmpv6_error_helper(
4248            buffer.as_mut(),
4249            Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4250            Icmpv6ParameterProblem::new(0),
4251            |CtxPair { core_ctx, bindings_ctx: _ }| {
4252                assert_eq!(core_ctx.icmp.rx_counters.error.get(), 1);
4253                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_transport_layer.get(), 0);
4254                assert_eq!(core_ctx.icmp.rx_counters.error_delivered_to_socket.get(), 0);
4255                let err = Icmpv6ErrorCode::ParameterProblem(
4256                    Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
4257                );
4258                assert_eq!(core_ctx.icmp.receive_icmp_error, [err]);
4259            },
4260        );
4261    }
4262
4263    #[test]
4264    fn test_error_rate_limit() {
4265        set_logger_for_test();
4266
4267        /// Call `send_icmpv4_ttl_expired` with fake values.
4268        fn send_icmpv4_ttl_expired_helper(
4269            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4270        ) {
4271            send_icmpv4_ttl_expired(
4272                core_ctx,
4273                bindings_ctx,
4274                &FakeDeviceId,
4275                Some(FrameDestination::Individual { local: true }),
4276                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4277                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4278                IpProto::Udp.into(),
4279                EmptyBuf,
4280                0,
4281                Ipv4FragmentType::InitialFragment,
4282                &Default::default(),
4283            );
4284        }
4285
4286        /// Call `send_icmpv4_parameter_problem` with fake values.
4287        fn send_icmpv4_parameter_problem_helper(
4288            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4289        ) {
4290            send_icmpv4_parameter_problem(
4291                core_ctx,
4292                bindings_ctx,
4293                &FakeDeviceId,
4294                Some(FrameDestination::Individual { local: true }),
4295                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4296                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4297                Icmpv4ParameterProblemCode::PointerIndicatesError,
4298                Icmpv4ParameterProblem::new(0),
4299                EmptyBuf,
4300                0,
4301                Ipv4FragmentType::InitialFragment,
4302                &Default::default(),
4303            );
4304        }
4305
4306        /// Call `send_icmpv4_dest_unreachable` with fake values.
4307        fn send_icmpv4_dest_unreachable_helper(
4308            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv4>,
4309        ) {
4310            send_icmpv4_dest_unreachable(
4311                core_ctx,
4312                bindings_ctx,
4313                Some(&FakeDeviceId),
4314                Some(FrameDestination::Individual { local: true }),
4315                TEST_ADDRS_V4.remote_ip.try_into().unwrap(),
4316                TEST_ADDRS_V4.local_ip.try_into().unwrap(),
4317                Icmpv4DestUnreachableCode::DestNetworkUnreachable,
4318                EmptyBuf,
4319                0,
4320                Ipv4FragmentType::InitialFragment,
4321                &Default::default(),
4322            );
4323        }
4324
4325        /// Call `send_icmpv6_ttl_expired` with fake values.
4326        fn send_icmpv6_ttl_expired_helper(
4327            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4328        ) {
4329            send_icmpv6_ttl_expired(
4330                core_ctx,
4331                bindings_ctx,
4332                &FakeDeviceId,
4333                Some(FrameDestination::Individual { local: true }),
4334                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4335                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4336                IpProto::Udp.into(),
4337                EmptyBuf,
4338                0,
4339                &Default::default(),
4340            );
4341        }
4342
4343        /// Call `send_icmpv6_packet_too_big` with fake values.
4344        fn send_icmpv6_packet_too_big_helper(
4345            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4346        ) {
4347            send_icmpv6_packet_too_big(
4348                core_ctx,
4349                bindings_ctx,
4350                &FakeDeviceId,
4351                Some(FrameDestination::Individual { local: true }),
4352                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4353                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4354                IpProto::Udp.into(),
4355                Mtu::new(0),
4356                EmptyBuf,
4357                0,
4358                &Default::default(),
4359            );
4360        }
4361
4362        /// Call `send_icmpv6_parameter_problem` with fake values.
4363        fn send_icmpv6_parameter_problem_helper(
4364            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4365        ) {
4366            send_icmpv6_parameter_problem(
4367                core_ctx,
4368                bindings_ctx,
4369                &FakeDeviceId,
4370                Some(FrameDestination::Individual { local: true }),
4371                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4372                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4373                Icmpv6ParameterProblemCode::ErroneousHeaderField,
4374                Icmpv6ParameterProblem::new(0),
4375                EmptyBuf,
4376                false,
4377                &Default::default(),
4378            );
4379        }
4380
4381        /// Call `send_icmpv6_dest_unreachable` with fake values.
4382        fn send_icmpv6_dest_unreachable_helper(
4383            CtxPair { core_ctx, bindings_ctx }: &mut FakeIcmpCtx<Ipv6>,
4384        ) {
4385            send_icmpv6_dest_unreachable(
4386                core_ctx,
4387                bindings_ctx,
4388                Some(&FakeDeviceId),
4389                Some(FrameDestination::Individual { local: true }),
4390                TEST_ADDRS_V6.remote_ip.try_into().unwrap(),
4391                TEST_ADDRS_V6.local_ip.try_into().unwrap(),
4392                Icmpv6DestUnreachableCode::NoRoute,
4393                EmptyBuf,
4394                &Default::default(),
4395            );
4396        }
4397
4398        // Run tests for each function that sends error messages to make sure
4399        // they're all properly rate limited.
4400
4401        fn run_test<I: IpExt, W: Fn(u64) -> FakeIcmpCtx<I>, S: Fn(&mut FakeIcmpCtx<I>)>(
4402            with_errors_per_second: W,
4403            send: S,
4404        ) {
4405            // Note that we could theoretically have more precise tests here
4406            // (e.g., a test that we send at the correct rate over the long
4407            // term), but those would amount to testing the `TokenBucket`
4408            // implementation, which has its own exhaustive tests. Instead, we
4409            // just have a few sanity checks to make sure that we're actually
4410            // invoking it when we expect to (as opposed to bypassing it
4411            // entirely or something).
4412
4413            // Test that, if no time has elapsed, we can successfully send up to
4414            // `ERRORS_PER_SECOND` error messages, but no more.
4415
4416            // Don't use `DEFAULT_ERRORS_PER_SECOND` because it's 2^16 and it
4417            // makes this test take a long time.
4418            const ERRORS_PER_SECOND: u64 = 64;
4419
4420            let mut ctx = with_errors_per_second(ERRORS_PER_SECOND);
4421
4422            for i in 0..ERRORS_PER_SECOND {
4423                send(&mut ctx);
4424                assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), i + 1);
4425            }
4426
4427            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
4428            send(&mut ctx);
4429            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), ERRORS_PER_SECOND);
4430
4431            // Test that, if we set a rate of 0, we are not able to send any
4432            // error messages regardless of how much time has elapsed.
4433
4434            let mut ctx = with_errors_per_second(0);
4435            send(&mut ctx);
4436            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4437            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4438            send(&mut ctx);
4439            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4440            ctx.bindings_ctx.timers.instant.sleep(Duration::from_secs(1));
4441            send(&mut ctx);
4442            assert_eq!(ctx.core_ctx.icmp.tx_counters.error.get(), 0);
4443        }
4444
4445        fn with_errors_per_second_v4(errors_per_second: u64) -> FakeIcmpCtx<Ipv4> {
4446            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4447        }
4448        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_ttl_expired_helper);
4449        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_parameter_problem_helper);
4450        run_test::<Ipv4, _, _>(with_errors_per_second_v4, send_icmpv4_dest_unreachable_helper);
4451
4452        fn with_errors_per_second_v6(errors_per_second: u64) -> FakeIcmpCtx<Ipv6> {
4453            CtxPair::with_core_ctx(FakeIcmpCoreCtx::with_errors_per_second(errors_per_second))
4454        }
4455
4456        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_ttl_expired_helper);
4457        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_packet_too_big_helper);
4458        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_parameter_problem_helper);
4459        run_test::<Ipv6, _, _>(with_errors_per_second_v6, send_icmpv6_dest_unreachable_helper);
4460    }
4461}