Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets
From: Eric Dumazet <edumazet@google.com>
Date: 2026-06-01 07:33:03
Also in:
lkml
On Mon, Jun 1, 2026 at 12:23 AM Sayooj Karun [off-list ref] 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 [off-list ref] wrote:quoted
On Wed, May 27, 2026 at 2:46 AM Sayooj K Karun [off-list ref] wrote:quoted
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 <redacted> --- 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-prohibitedYou 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?quoted
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