[netstack3][icmp] Factor out ICMP error message logic

All ICMP error messages are sent by calling `IcmpContext`'s
`send_icmp_error_message` method. Instead of calling
`should_send_icmpv4_error` and `should_send_icmpv6_error` from each
top-level function separately, move that logic into
`send_icmp_error_message`.

Test: Existing ICMP tests pass
Change-Id: I2b21b58d18cfadc7bb096be73116dd7733dab77d
diff --git a/src/connectivity/network/netstack3/core/src/device/ndp.rs b/src/connectivity/network/netstack3/core/src/device/ndp.rs
index 61406fc..3766d375 100644
--- a/src/connectivity/network/netstack3/core/src/device/ndp.rs
+++ b/src/connectivity/network/netstack3/core/src/device/ndp.rs
@@ -1971,7 +1971,6 @@
         dst_ip: Ipv6Addr,
         packet: Icmpv6Packet<B>,
     ) {
-
     }
 }
 
diff --git a/src/connectivity/network/netstack3/core/src/ip/icmp.rs b/src/connectivity/network/netstack3/core/src/ip/icmp.rs
index 69f9fbb..be41026 100644
--- a/src/connectivity/network/netstack3/core/src/ip/icmp.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/icmp.rs
@@ -183,6 +183,17 @@
     /// and how to send an ICMP error message. `ip_mtu` is an optional MTU size
     /// for the final IP packet generated by this ICMP response.
     ///
+    /// `send_icmp_error_message` is responsible for calling
+    /// [`should_send_icmpv4_error`] or [`should_send_icmpv6_error`]. If those
+    /// return `false`, then it must not send the message regardless of whatever
+    /// other logic is used.
+    ///
+    /// If the encapsulated error message is an ICMPv6 Packet Too Big Message or
+    /// a Parameter Problem Message, Code 2 reporting an unrecognized IPv6
+    /// option that has the Option Type highest-order two bits set to 10,
+    /// `allow_dst_multicast` must be set to `true`. See
+    /// [`should_send_icmpv6_error`] for more details.
+    ///
     /// get_body` returns a `Serializer` with the bytes of the ICMP packet, and,
     /// when called, is given the source IP address chosen for the outbound
     /// packet. This allows `get_body` to properly compute the ICMP checksum,
@@ -191,10 +202,12 @@
     fn send_icmp_error_message<S: Serializer<Buffer = B>, F: FnOnce(I::Addr) -> S>(
         &mut self,
         device: Self::DeviceId,
+        frame_dst: FrameDestination,
         src_ip: I::Addr,
         dst_ip: I::Addr,
         get_body: F,
         ip_mtu: Option<u32>,
+        allow_dst_multicast: bool,
     ) -> Result<(), S>;
 }
 
@@ -468,17 +481,10 @@
 ) {
     ctx.increment_counter("send_icmpv4_protocol_unreachable");
 
-    // Check whether we MUST NOT send an ICMP error message. Unlike other
-    // send_icmpv4_xxx functions, we do not check to see whether the inbound
-    // packet is an ICMP error message - we already know it's not since its
-    // protocol was unsupported.
-    if !should_send_icmpv4_error(frame_dst, src_ip, dst_ip) {
-        return;
-    }
-
     send_icmpv4_dest_unreachable(
         ctx,
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         Icmpv4DestUnreachableCode::DestProtocolUnreachable,
@@ -508,17 +514,10 @@
 ) {
     ctx.increment_counter("send_icmpv6_protocol_unreachable");
 
-    // Check whether we MUST NOT send an ICMP error message. Unlike other
-    // send_icmpv6_xxx functions, we do not check to see whether the inbound
-    // packet is an ICMP error message - we already know it's not since its
-    // protocol was unsupported.
-    if !should_send_icmpv6_error(frame_dst, src_ip, dst_ip, false) {
-        return;
-    }
-
     send_icmpv6_parameter_problem(
         ctx,
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         Icmpv6ParameterProblemCode::UnrecognizedNextHeaderType,
@@ -538,6 +537,7 @@
         Icmpv6ParameterProblem::new(header_len as u32),
         original_packet,
         header_len,
+        false,
     );
 }
 
@@ -563,17 +563,10 @@
 ) {
     ctx.increment_counter("send_icmpv4_port_unreachable");
 
-    // Check whether we MUST NOT send an ICMP error message. Unlike other
-    // send_icmpv4_xxx functions, we do not check to see whether the inbound
-    // packet is an ICMP error message - we already know it's not since ICMP is
-    // not one of the protocols that can generate "port unreachable" errors.
-    if !should_send_icmpv4_error(frame_dst, src_ip, dst_ip) {
-        return;
-    }
-
     send_icmpv4_dest_unreachable(
         ctx,
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         Icmpv4DestUnreachableCode::DestPortUnreachable,
@@ -600,17 +593,10 @@
 ) {
     ctx.increment_counter("send_icmpv6_port_unreachable");
 
-    // Check whether we MUST NOT send an ICMP error message. Unlike other
-    // send_icmpv4_xxx functions, we do not check to see whether the inbound
-    // packet is an ICMP error message - we already know it's not since ICMP is
-    // not one of the protocols that can generate "port unreachable" errors.
-    if !should_send_icmpv6_error(frame_dst, src_ip, dst_ip, false) {
-        return;
-    }
-
     send_icmpv6_dest_unreachable(
         ctx,
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         Icmpv6DestUnreachableCode::PortUnreachable,
@@ -641,18 +627,16 @@
 ) {
     ctx.increment_counter("send_icmpv4_net_unreachable");
 
-    // Check whether we MUST NOT send an ICMP error message.
-    // should_send_icmpv4_error does not handle the "ICMP error message" case,
-    // so we check that separately with a call to is_icmp_error_message.
-    if !should_send_icmpv4_error(frame_dst, src_ip, dst_ip)
-        || is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..])
-    {
+    // Check whether we MUST NOT send an ICMP error message
+    // because the original packet was itself an ICMP error message.
+    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
         return;
     }
 
     send_icmpv4_dest_unreachable(
         ctx,
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         Icmpv4DestUnreachableCode::DestNetworkUnreachable,
@@ -683,18 +667,16 @@
 ) {
     ctx.increment_counter("send_icmpv6_net_unreachable");
 
-    // Check whether we MUST NOT send an ICMPv6 error message.
-    // should_send_icmpv6_error does not handle the "ICMP error message" case,
-    // so we check that separately with a call to is_icmp_error_message.
-    if !should_send_icmpv6_error(frame_dst, src_ip, dst_ip, false)
-        || is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..])
-    {
+    // Check whether we MUST NOT send an ICMP error message
+    // because the original packet was itself an ICMP error message.
+    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
         return;
     }
 
     send_icmpv6_dest_unreachable(
         ctx,
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         Icmpv6DestUnreachableCode::NoRoute,
@@ -725,12 +707,9 @@
 ) {
     ctx.increment_counter("send_icmpv4_ttl_expired");
 
-    // Check whether we MUST NOT send an ICMP error message.
-    // should_send_icmpv4_error does not handle the "ICMP error message" case,
-    // so we check that separately with a call to is_icmp_error_message.
-    if !should_send_icmpv4_error(frame_dst, src_ip, dst_ip)
-        || is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..])
-    {
+    // Check whether we MUST NOT send an ICMP error message
+    // because the original packet was itself an ICMP error message.
+    if is_icmp_error_message::<Ipv4>(proto, &original_packet.as_ref()[header_len..]) {
         return;
     }
 
@@ -740,6 +719,7 @@
     // TODO(joshlf): Do something if send_icmp_error_message returns an error?
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -751,6 +731,7 @@
             ))
         },
         None,
+        false,
     );
 }
 
@@ -776,18 +757,16 @@
 ) {
     ctx.increment_counter("send_icmpv6_ttl_expired");
 
-    // Check whether we MUST NOT send an ICMP error message.
-    // should_send_icmpv6_error does not handle the "ICMP error message" case,
-    // so we check that separately with a call to is_icmp_error_message.
-    if !should_send_icmpv6_error(frame_dst, src_ip, dst_ip, false)
-        || is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..])
-    {
+    // Check whether we MUST NOT send an ICMP error message
+    // because the original packet was itself an ICMP error message.
+    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
         return;
     }
 
     // TODO(joshlf): Do something if send_icmp_error_message returns an error?
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -804,6 +783,7 @@
                 .encapsulate(icmp_builder)
         },
         Some(IPV6_MIN_MTU),
+        false,
     );
 }
 
@@ -829,23 +809,15 @@
 ) {
     ctx.increment_counter("send_icmpv6_packet_too_big");
 
-    // Check whether we MUST NOT send an ICMP error message.
-    // should_send_icmp_error does not handle the "ICMP error message" case, so
-    // we check that separately with a call to is_icmp_error_message.
-    //
-    // Note, here we explicitly let `should_send_icmpv6_error` allow a multicast
-    // destination (link-layer or destination ip) as RFC 4443 Section 2.4.e
-    // explicitly allows sending an ICMP response if the original packet was
-    // sent to a multicast IP or link layer if the ICMP response message will be
-    // a Packet Too Big Message.
-    if !should_send_icmpv6_error(frame_dst, src_ip, dst_ip, true)
-        || is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..])
-    {
+    // Check whether we MUST NOT send an ICMP error message
+    // because the original packet was itself an ICMP error message.
+    if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
         return;
     }
 
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -865,12 +837,19 @@
                 .encapsulate(icmp_builder)
         },
         Some(IPV6_MIN_MTU),
+        // Note, here we explicitly let `should_send_icmpv6_error` allow a
+        // multicast destination (link-layer or destination IP) as RFC 4443
+        // Section 2.4.e explicitly allows sending an ICMP response if the
+        // original packet was sent to a multicast IP or link layer if the ICMP
+        // response message will be a Packet Too Big Message.
+        true,
     );
 }
 
 pub(crate) fn send_icmpv4_parameter_problem<B: BufferMut, C: IcmpContext<Ipv4, B>>(
     ctx: &mut C,
     device: C::DeviceId,
+    frame_dst: FrameDestination,
     src_ip: Ipv4Addr,
     dst_ip: Ipv4Addr,
     code: Icmpv4ParameterProblemCode,
@@ -886,6 +865,7 @@
     // TODO(joshlf): Do something if send_icmp_error_message returns an error?
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -897,24 +877,43 @@
             ))
         },
         None,
+        false,
     );
 }
 
+/// Send an ICMPv6 Parameter Problem error message.
+///
+/// If the error message is Code 2 reporting an unrecognized IPv6 option that has the Option
+/// Type highest-order two bits set to 10, `allow_dst_multicast` must be set to `true`. See
+/// [`should_send_icmpv6_error`] for more details.
+///
+/// # Panics
+///
+/// Panics if `allow_multicast_addr` is set to `true`, but this Parameter Problem's code is not
+/// 2 (Unrecognized IPv6 Option).
 pub(crate) fn send_icmpv6_parameter_problem<B: BufferMut, C: IcmpContext<Ipv6, B>>(
     ctx: &mut C,
     device: C::DeviceId,
+    frame_dst: FrameDestination,
     src_ip: Ipv6Addr,
     dst_ip: Ipv6Addr,
     code: Icmpv6ParameterProblemCode,
     parameter_problem: Icmpv6ParameterProblem,
     original_packet: B,
     header_len: usize,
+    allow_multicast_dst: bool,
 ) {
+    // Only allow the `allow_multicast_dst` parameter to be set if the code is the unrecognized
+    // IPv6 option as that is one of the few exceptions where we can send an ICMP packet in response
+    // to a packet that was destined for a multicast address.
+    assert!(!allow_multicast_dst || code == Icmpv6ParameterProblemCode::UnrecognizedIpv6Option);
+
     ctx.increment_counter("send_icmpv6_parameter_problem");
 
     // TODO(joshlf): Do something if send_icmp_error_message returns an error?
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -927,12 +926,14 @@
                 .encapsulate(icmp_builder)
         },
         Some(IPV6_MIN_MTU),
+        allow_multicast_dst,
     );
 }
 
 fn send_icmpv4_dest_unreachable<B: BufferMut, C: IcmpContext<Ipv4, B>>(
     ctx: &mut C,
     device: C::DeviceId,
+    frame_dst: FrameDestination,
     src_ip: Ipv4Addr,
     dst_ip: Ipv4Addr,
     code: Icmpv4DestUnreachableCode,
@@ -947,6 +948,7 @@
     // TODO(joshlf): Do something if send_icmp_error_message returns an error?
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -958,12 +960,14 @@
             ))
         },
         None,
+        false,
     );
 }
 
 fn send_icmpv6_dest_unreachable<B: BufferMut, C: IcmpContext<Ipv6, B>>(
     ctx: &mut C,
     device: C::DeviceId,
+    frame_dst: FrameDestination,
     src_ip: Ipv6Addr,
     dst_ip: Ipv6Addr,
     code: Icmpv6DestUnreachableCode,
@@ -972,6 +976,7 @@
     // TODO(joshlf): Do something if send_icmp_error_message returns an error?
     ctx.send_icmp_error_message(
         device,
+        frame_dst,
         src_ip,
         dst_ip,
         |local_ip| {
@@ -988,6 +993,7 @@
                 .encapsulate(icmp_builder)
         },
         Some(IPV6_MIN_MTU),
+        false,
     );
 }
 
diff --git a/src/connectivity/network/netstack3/core/src/ip/mod.rs b/src/connectivity/network/netstack3/core/src/ip/mod.rs
index 90e79e3..1ebb7e9 100644
--- a/src/connectivity/network/netstack3/core/src/ip/mod.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/mod.rs
@@ -34,9 +34,8 @@
 use crate::error::{ExistsError, IpParseError, NotFoundError};
 use crate::ip::forwarding::{Destination, ForwardingTable};
 use crate::ip::icmp::{
-    send_icmpv4_parameter_problem, send_icmpv6_parameter_problem, should_send_icmpv4_error,
-    should_send_icmpv6_error, IcmpContext, IcmpEventDispatcher, IcmpState, IcmpStateBuilder,
-    Icmpv4State, Icmpv6State,
+    send_icmpv4_parameter_problem, send_icmpv6_parameter_problem, IcmpContext, IcmpEventDispatcher,
+    IcmpState, IcmpStateBuilder, Icmpv4State, Icmpv6State,
 };
 use crate::ip::igmp::{IgmpContext, IgmpInterface, IgmpPacketMetadata, IgmpTimerId};
 use crate::ip::ipv6::Ipv6PacketAction;
@@ -1045,45 +1044,32 @@
                     // This should never return `true` for IPv4.
                     assert!(!action.should_send_icmp_to_multicast());
 
-                    if should_send_icmpv4_error(frame_dst, src_ip, dst_ip) {
-                        send_icmpv4_parameter_problem(
-                            ctx,
-                            device,
-                            src_ip,
-                            dst_ip,
-                            code,
-                            Icmpv4ParameterProblem::new(pointer),
-                            original_packet,
-                            header_len,
-                        );
-                    }
-                }
-
-                #[ipv6]
-                {
-                    // Some IPv6 parsing errors may require us to send an
-                    // ICMP response even if the original packet's destination
-                    // was a multicast (as defined by RFC 4443 section 2.4.e).
-                    // `action.should_send_icmp_to_multicast()` should return
-                    // `true` if such an exception applies.
-                    if should_send_icmpv6_error(
+                    send_icmpv4_parameter_problem(
+                        ctx,
+                        device,
                         frame_dst,
                         src_ip,
                         dst_ip,
-                        action.should_send_icmp_to_multicast(),
-                    ) {
-                        send_icmpv6_parameter_problem(
-                            ctx,
-                            device,
-                            src_ip,
-                            dst_ip,
-                            code,
-                            Icmpv6ParameterProblem::new(pointer),
-                            original_packet,
-                            header_len,
-                        );
-                    }
+                        code,
+                        Icmpv4ParameterProblem::new(pointer),
+                        original_packet,
+                        header_len,
+                    );
                 }
+
+                #[ipv6]
+                send_icmpv6_parameter_problem(
+                    ctx,
+                    device,
+                    frame_dst,
+                    src_ip,
+                    dst_ip,
+                    code,
+                    Icmpv6ParameterProblem::new(pointer),
+                    original_packet,
+                    header_len,
+                    action.should_send_icmp_to_multicast(),
+                );
             }
         }
         // TODO(joshlf): Do something with ICMP here? If not, then just turn
@@ -1494,10 +1480,12 @@
     fn send_icmp_error_message<S: Serializer<Buffer = B>, F: FnOnce(I::Addr) -> S>(
         &mut self,
         device: DeviceId,
+        frame_dst: FrameDestination,
         src_ip: I::Addr,
         dst_ip: I::Addr,
         get_body: F,
         ip_mtu: Option<u32>,
+        allow_dst_multicast: bool,
     ) -> Result<(), S> {
         trace!("send_icmp_error_message({}, {}, {}, {:?})", device, src_ip, dst_ip, ip_mtu);
         self.increment_counter("send_icmp_error_message");
@@ -1507,6 +1495,28 @@
         // device that the original packet ingressed over? We'll probably want
         // to consult BCP 38 (aka RFC 2827) and RFC 3704.
 
+        #[specialize_ip_address]
+        fn should_send_icmp_error<A: IpAddress>(
+            frame_dst: FrameDestination,
+            src_ip: A,
+            dst_ip: A,
+            allow_dst_multicast: bool,
+        ) -> bool {
+            #[ipv4addr]
+            return crate::ip::icmp::should_send_icmpv4_error(frame_dst, src_ip, dst_ip);
+            #[ipv6addr]
+            return crate::ip::icmp::should_send_icmpv6_error(
+                frame_dst,
+                src_ip,
+                dst_ip,
+                allow_dst_multicast,
+            );
+        }
+
+        if !should_send_icmp_error(frame_dst, src_ip, dst_ip, allow_dst_multicast) {
+            return Ok(());
+        }
+
         if let Some(route) = lookup_route(self, src_ip) {
             if let Some(local_ip) =
                 crate::device::get_ip_addr_subnet(self, route.device).map(AddrSubnet::into_addr)
@@ -1609,11 +1619,12 @@
         ctx: &mut Context<DummyEventDispatcher>,
         code: Icmpv6ParameterProblemCode,
         pointer: u32,
+        offset: usize,
     ) {
         // Check the ICMP that bob attempted to send to alice
         let device_frames = ctx.dispatcher.frames_sent().clone();
         assert!(!device_frames.is_empty());
-        let mut buffer = Buf::new(device_frames[0].1.as_slice(), ..);
+        let mut buffer = Buf::new(device_frames[offset].1.as_slice(), ..);
         let frame = buffer.parse::<EthernetFrame<_>>().unwrap();
         let packet = buffer.parse::<<Ipv6 as IpExtByteSlice<&[u8]>>::Packet>().unwrap();
         let (src_ip, dst_ip, proto, _) = drop_packet!(packet);
@@ -1844,10 +1855,12 @@
         bytes[24..40].copy_from_slice(DUMMY_CONFIG_V6.local_ip.bytes());
         let mut buf = Buf::new(bytes, ..);
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, FrameDestination::Unicast, buf);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), 1);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut ctx,
             Icmpv6ParameterProblemCode::ErroneousHeaderField,
             42,
+            0,
         );
     }
 
@@ -1878,6 +1891,7 @@
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, frame_dst, buf);
         assert_eq!(get_counter_val(&mut ctx, "send_icmpv6_parameter_problem"), expected_icmps);
         assert_eq!(get_counter_val(&mut ctx, "dispatch_receive_ip_packet"), 1);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), expected_icmps);
 
         //
         // Test with unrecognized option type set with
@@ -1891,6 +1905,7 @@
         );
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, frame_dst, buf);
         assert_eq!(get_counter_val(&mut ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), expected_icmps);
 
         //
         // Test with unrecognized option type set with
@@ -1906,10 +1921,12 @@
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, frame_dst, buf);
         expected_icmps += 1;
         assert_eq!(get_counter_val(&mut ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), expected_icmps);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut ctx,
             Icmpv6ParameterProblemCode::UnrecognizedIpv6Option,
             48,
+            expected_icmps - 1,
         );
 
         //
@@ -1926,10 +1943,12 @@
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, frame_dst, buf);
         expected_icmps += 1;
         assert_eq!(get_counter_val(&mut ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), expected_icmps);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut ctx,
             Icmpv6ParameterProblemCode::UnrecognizedIpv6Option,
             48,
+            expected_icmps - 1,
         );
 
         //
@@ -1946,10 +1965,12 @@
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, frame_dst, buf);
         expected_icmps += 1;
         assert_eq!(get_counter_val(&mut ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), expected_icmps);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut ctx,
             Icmpv6ParameterProblemCode::UnrecognizedIpv6Option,
             48,
+            expected_icmps - 1,
         );
 
         //
@@ -1966,6 +1987,7 @@
         // Do not expect an ICMP response for this packet
         receive_ip_packet::<_, _, Ipv6>(&mut ctx, device, frame_dst, buf);
         assert_eq!(get_counter_val(&mut ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(ctx.dispatcher().frames_sent().len(), expected_icmps);
 
         //
         // None of our tests should have sent an icmpv4 packet, or dispatched an ip packet
diff --git a/src/connectivity/network/netstack3/core/src/wire/ipv6/mod.rs b/src/connectivity/network/netstack3/core/src/wire/ipv6/mod.rs
index c46b0f3..4e537fa 100644
--- a/src/connectivity/network/netstack3/core/src/wire/ipv6/mod.rs
+++ b/src/connectivity/network/netstack3/core/src/wire/ipv6/mod.rs
@@ -82,7 +82,7 @@
                 // Pointer calculation didn't overflow so set action to send an ICMP
                 // message to the source of the original packet and the pointer value
                 // to what we calculated.
-                Some(p) => (p, IpParseErrorAction::DiscardPacketSendICMP),
+                Some(p) => (p, IpParseErrorAction::DiscardPacketSendICMPNoMulticast),
             };
 
             IpParseError::ParameterProblem {
@@ -102,7 +102,7 @@
         } => {
             let (pointer, action) = match pointer.checked_add(IPV6_FIXED_HDR_LEN as u32) {
                 None => (0, IpParseErrorAction::DiscardPacket),
-                Some(p) => (p, IpParseErrorAction::DiscardPacketSendICMP),
+                Some(p) => (p, IpParseErrorAction::DiscardPacketSendICMPNoMulticast),
             };
 
             IpParseError::ParameterProblem {
@@ -234,7 +234,7 @@
                     pointer: NEXT_HEADER_OFFSET as u32,
                     must_send_icmp: false,
                     header_len: IPV6_FIXED_HDR_LEN,
-                    action: IpParseErrorAction::DiscardPacketSendICMP,
+                    action: IpParseErrorAction::DiscardPacketSendICMPNoMulticast,
                 }),
                 "Unrecognized next header value"
             );
@@ -1035,7 +1035,7 @@
                 pointer: NEXT_HEADER_OFFSET as u32,
                 must_send_icmp: false,
                 header_len: IPV6_FIXED_HDR_LEN,
-                action: IpParseErrorAction::DiscardPacketSendICMP,
+                action: IpParseErrorAction::DiscardPacketSendICMPNoMulticast,
             }
         );
 
@@ -1051,7 +1051,7 @@
                 pointer: NEXT_HEADER_OFFSET as u32,
                 must_send_icmp: false,
                 header_len: IPV6_FIXED_HDR_LEN,
-                action: IpParseErrorAction::DiscardPacketSendICMP,
+                action: IpParseErrorAction::DiscardPacketSendICMPNoMulticast,
             }
         );
 
@@ -1097,7 +1097,7 @@
                 pointer: IPV6_FIXED_HDR_LEN as u32,
                 must_send_icmp: false,
                 header_len: IPV6_FIXED_HDR_LEN,
-                action: IpParseErrorAction::DiscardPacketSendICMP,
+                action: IpParseErrorAction::DiscardPacketSendICMPNoMulticast,
             }
         );
 
@@ -1136,7 +1136,7 @@
                 pointer: (IPV6_FIXED_HDR_LEN as u32) + 2,
                 must_send_icmp: true,
                 header_len: IPV6_FIXED_HDR_LEN,
-                action: IpParseErrorAction::DiscardPacketSendICMP,
+                action: IpParseErrorAction::DiscardPacketSendICMPNoMulticast,
             }
         );
     }