Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets
From: Eric Dumazet
Date: Wed May 27 2026 - 08:17:50 EST
On Wed, May 27, 2026 at 2:46 AM Sayooj K Karun <sayooj@xxxxxxxxxxx> 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 <sayooj@xxxxxxxxxxx>
> ---
> 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
>