[PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets
From: Sayooj K Karun
Date: Wed May 27 2026 - 05:56:03 EST
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
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