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 --- 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 -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