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