Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets
From: Eric Dumazet
Date: Mon Jun 01 2026 - 03:37:07 EST
On Mon, Jun 1, 2026 at 12:23 AM Sayooj Karun <sayooj@xxxxxxxxxxx> 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?
>
> 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.
>
> Thanks,
> Sayooj
>
> On Wed, May 27, 2026 at 5:45 PM Eric Dumazet <edumazet@xxxxxxxxxx> wrote:
>>
>> 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
>> >