Subject: Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets

From: Pablo Neira Ayuso <[email-masked]>
Date: Mon, 1 Jun 2026 11:49:23 +0200

Hi,

On Mon, Jun 01, 2026 at 12:32:50AM -0700, Eric Dumazet wrote:
> On Mon, Jun 1, 2026 at 12:23 AM Sayooj Karun <[email-masked]> wrote:
> >
> > Hi Eric,
> >
> > Thank you for the review.
> > The concern about REJECT becoming DROP for Redirect packets is correct, and that is intentional.
> > RFC 4443  section 2.4(e.2) mandates unconditionally that the originator MUST NOT send an ICMPv6 error in response to a Redirect message.
> > There is no exception for netfilter policy.
>
> Really? Have you CC netfilter maintainers?

Thanks for Cc'ing.

> > To your question "how would the kernel send a reply?" : it cannot, as per the RFC.
> > A user who writes -j REJECT on ICMPv6 Redirect naturally expects an error to be sent back, but the protocol leaves no room for that.
> > RFC 4443  Section 2.4(e.2) forbids it outright. The pre-patch behavior (sending Destination Unreachable in response to Redirect) was the RFC violation.
> >
> > The same suppression already exists in is_ineligible() for section 2.4(e.1): a REJECT rule targeting any ICMPv6 error type (types 0–127) already silently drops without generating an error, because !(*tp & ICMPV6_INFOMSG_MASK)
> > returns true. This patch applies the same logic to  section 2.4(e.2) (Redirect, type 137). Both are entries in the same "MUST NOT" list in RFC 4443.
> > If the goal is to block incoming Redirects, -j DROP is the right tool. Using -j REJECT on Redirect packets was never RFC-compliant. The error response it tried to send was never allowed by the protocol.

Netfilter, through policy, allows to modify the box behaviour in
different ways that makes it non-RFC compliant.

But there is code already code such as nf_skb_is_icmp_unreach() which
does not allow to reject a icmp dest unreach with another icmp dest
unreach.

Also looking at your patch, how does it fix the REJECT case? Maybe you
can propose a patch for nf-next as enhancement that can be reviewed to
[email-masked].

Thanks.

> > Thanks,
> > Sayooj
> >
> > On Wed, May 27, 2026 at 5:45 PM Eric Dumazet <[email-masked]> wrote:
> >>
> >> On Wed, May 27, 2026 at 2:46 AM Sayooj K Karun <[email-masked]> wrote:
> >> >
> >> > Discovered while reading RFC 4443 and cross-checking the ICMPv6 stack,
> >> > which in section 2.4(e.2) mandates that an ICMPv6 error MUST NOT be
> >> > originated in response to an ICMPv6 Redirect message (type 137).
> >> >
> >> > is_ineligible() is called by icmpv6_send() to decide whether a received
> >> > packet is eligible to trigger an ICMPv6 error. It uses
> >> > ICMPV6_INFOMSG_MASK (0x80) to suppress errors for ICMPv6 error types
> >> > (bit 7 clear) while passing informational types (bit 7 set). However,
> >> > NDISC_REDIRECT has type 137 (0x89), which has bit 7 set, so it passes
> >> > the mask check and is_ineligible() returns false and icmpv6_send()
> >> > proceeds to generate an error in response to a Redirect, violating the
> >> > RFC.
> >> >
> >> > A triggerable scenario: a host with an ip6tables/nftables REJECT rule
> >> > applied to incoming ICMPv6 traffic (e.g., dropping Redirects from an
> >> > untrusted router). When the Redirect hits the REJECT rule,
> >> > nf_send_unreach6() calls icmpv6_send() with the Redirect as the
> >> > triggering skb. Without this fix, is_ineligible() returns false and a
> >> > Destination Unreachable is erroneously transmitted in response.
> >> >
> >> > Add an explicit check for NDISC_REDIRECT so that Redirect packets are
> >> > treated as ineligible, suppressing ICMPv6 error generation in response
> >> > to them.
> >> >
> >> > Signed-off-by: Sayooj K Karun <[email-masked]>
> >> > ---
> >> > The bug can be triggered via a netfilter REJECT rule targeting ICMPv6
> >> > Redirect packets. Consider a host with the following rule:
> >> >
> >> >   ip6tables -A INPUT -s <untrusted-router> -p icmpv6 --icmpv6-type redirect \
> >> >             -j REJECT --reject-with icmp6-adm-prohibited
> >> >
> >>
> >> You are saying that after your patch, this ip6ables will no longer
> >> send a packet, and will effectively DROP
> >> the packet.
> >> This might break user choice/expectations/policy.
> >> If the user wanted a DROP, they would have instead specified "-j DROP"
> >>
> >> Apart from the netfilter world which is free to implement its own
> >> interpretation of the RFC,
> >> how would the kernel send a reply?
> >>
> >>
> >> > When a Redirect packet (type 137) arrives from that source, the packet
> >> > enters ip6_input(), which invokes NF_HOOK(NF_INET_LOCAL_IN). The
> >> > netfilter framework iterates the registered hook entries via
> >> > nf_hook_slow(), reaches the ip6tables filter table, and evaluates the
> >> > rules. The incoming Redirect matches the rule, causing reject_tg6()
> >> > (net/ipv6/netfilter/ip6t_REJECT.c) to be called as the rule's target
> >> > action. reject_tg6() calls nf_send_unreach6(), which in turn calls
> >> > icmpv6_send() with the Redirect packet as the triggering skb.
> >> >
> >> > Inside icmpv6_send(), is_ineligible() is called to decide whether to
> >> > suppress the error. The function detects that the inner protocol is
> >> > IPPROTO_ICMPV6 and reads the ICMPv6 type byte. NDISC_REDIRECT is
> >> > type 137 (0x89). The check !(*tp & ICMPV6_INFOMSG_MASK) evaluates as
> >> > !(0x89 & 0x80) = !(0x80) = false, so is_ineligible() falls through and
> >> > returns false and the kernel proceeds to transmit a Destination Unreachable
> >> > in response to the Redirect, violating RFC 4443 section 2.4(e.2).
> >> >
> >> > Tested using network namespaces with the above ip6tables rule. Without
> >> > the fix, tcpdump confirms a Destination Unreachable is transmitted in
> >> > response to the Redirect. With the fix applied and verified under QEMU,
> >> > no error is generated.
> >> >
> >> > From the RFC:
> >> > (e) An ICMPv6 error message MUST NOT be originated as a result of
> >> >     receiving the following:
> >> >
> >> >     (e.1) An ICMPv6 error message.
> >> >
> >> >     (e.2) An ICMPv6 redirect message [IPv6-DISC].
> >> >
> >> >     ...
> >> >
> >> >  net/ipv6/icmp.c | 3 ++-
> >> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >> >
> >> > diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
> >> > index efb23807a026..3fdb3a97dd8e 100644
> >> > --- a/net/ipv6/icmp.c
> >> > +++ b/net/ipv6/icmp.c
> >> > @@ -157,7 +157,8 @@ static bool is_ineligible(const struct sk_buff *skb)
> >> >                  */
> >> >                 if (!tp && frag_off != 0)
> >> >                         return false;
> >> > -               else if (!tp || !(*tp & ICMPV6_INFOMSG_MASK))
> >> > +               else if (!tp || !(*tp & ICMPV6_INFOMSG_MASK) ||
> >> > +                        *tp == NDISC_REDIRECT)
> >> >                         return true;
> >> >         }
> >> >         return false;
> >> > --
> >> > 2.53.0
> >> >