* [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets @ 2026-05-26 10:16 Sayooj K Karun 2026-05-26 15:40 ` Alexander Lobakin 2026-05-27 9:46 ` [PATCH v2 net] " Sayooj K Karun 0 siblings, 2 replies; 8+ messages in thread From: Sayooj K Karun @ 2026-05-26 10:16 UTC (permalink / raw) To: netdev Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, linux-kernel, Sayooj K Karun RFC 4443 section 2.4(e.2) mandates that an ICMPv6 error message MUST NOT be originated in response to an ICMPv6 Redirect message (type 137). Add check for NDISC_REDIRECT (137) so that redirect packets become ineligible and error packets are not generated for Redirect messages. Signed-off-by: Sayooj K Karun <sayooj@aerlync.com> --- 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 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets 2026-05-26 10:16 [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets Sayooj K Karun @ 2026-05-26 15:40 ` Alexander Lobakin 2026-05-27 9:46 ` [PATCH v2 net] " Sayooj K Karun 1 sibling, 0 replies; 8+ messages in thread From: Alexander Lobakin @ 2026-05-26 15:40 UTC (permalink / raw) To: Sayooj K Karun Cc: netdev, dsahern, idosch, davem, edumazet, kuba, pabeni, horms, linux-kernel From: Sayooj K Karun <sayooj@aerlync.com> Date: Tue, 26 May 2026 15:46:22 +0530 > [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets Is this a fix or a feature? The subject prefix must contain either "net-next" (if targeting the feature tree) or "net" (if targeting the fixes tree) > RFC 4443 section 2.4(e.2) mandates that an ICMPv6 error message MUST NOT > be originated in response to an ICMPv6 Redirect message (type 137). > > Add check for NDISC_REDIRECT (137) so that redirect packets become > ineligible and error packets are not generated for Redirect messages. ...for I didn't understand from the commit message which tree this should be taken to and whether this should be considered for backporting. I'd also like to hear about real life scenarios where this misbehaves and what gets affected by this. IOW very poor description to be reviewable. > > Signed-off-by: Sayooj K Karun <sayooj@aerlync.com> > --- > 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; Thanks, Olek ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets 2026-05-26 10:16 [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets Sayooj K Karun 2026-05-26 15:40 ` Alexander Lobakin @ 2026-05-27 9:46 ` Sayooj K Karun 2026-05-27 12:15 ` Eric Dumazet 1 sibling, 1 reply; 8+ messages in thread From: Sayooj K Karun @ 2026-05-27 9:46 UTC (permalink / raw) To: netdev Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, linux-kernel, aleksander.lobakin, Sayooj K Karun 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@aerlync.com> --- 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 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets 2026-05-27 9:46 ` [PATCH v2 net] " Sayooj K Karun @ 2026-05-27 12:15 ` Eric Dumazet [not found] ` <CAFdLOz67Sxne4yXi3hB7Nf0eX=SzE9-P1phG4OzYfrgjQvmcGw@mail.gmail.com> 0 siblings, 1 reply; 8+ messages in thread From: Eric Dumazet @ 2026-05-27 12:15 UTC (permalink / raw) To: Sayooj K Karun Cc: netdev, dsahern, idosch, davem, kuba, pabeni, horms, linux-kernel, aleksander.lobakin On Wed, May 27, 2026 at 2:46 AM Sayooj K Karun <sayooj@aerlync.com> 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@aerlync.com> > --- > 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 > ^ permalink raw reply [flat|nested] 8+ messages in thread
[parent not found: <CAFdLOz67Sxne4yXi3hB7Nf0eX=SzE9-P1phG4OzYfrgjQvmcGw@mail.gmail.com>]
* Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets [not found] ` <CAFdLOz67Sxne4yXi3hB7Nf0eX=SzE9-P1phG4OzYfrgjQvmcGw@mail.gmail.com> @ 2026-06-01 7:32 ` Eric Dumazet 2026-06-01 9:49 ` Pablo Neira Ayuso 0 siblings, 1 reply; 8+ messages in thread From: Eric Dumazet @ 2026-06-01 7:32 UTC (permalink / raw) To: Sayooj Karun, Pablo Neira Ayuso, Florian Westphal Cc: netdev, dsahern, idosch, davem, kuba, pabeni, horms, linux-kernel, aleksander.lobakin On Mon, Jun 1, 2026 at 12:23 AM Sayooj Karun <sayooj@aerlync.com> 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@google.com> wrote: >> >> On Wed, May 27, 2026 at 2:46 AM Sayooj K Karun <sayooj@aerlync.com> 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@aerlync.com> >> > --- >> > 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 >> > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 net] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets 2026-06-01 7:32 ` Eric Dumazet @ 2026-06-01 9:49 ` Pablo Neira Ayuso 2026-06-03 6:01 ` [PATCH] " Sayooj K Karun 0 siblings, 1 reply; 8+ messages in thread From: Pablo Neira Ayuso @ 2026-06-01 9:49 UTC (permalink / raw) To: Sayooj Karun Cc: Eric Dumazet, Florian Westphal, netdev, dsahern, idosch, davem, kuba, pabeni, horms, linux-kernel, aleksander.lobakin Hi, On Mon, Jun 01, 2026 at 12:32:50AM -0700, Eric Dumazet wrote: > On Mon, Jun 1, 2026 at 12:23 AM Sayooj Karun <sayooj@aerlync.com> 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? Thanks for Cc'ing. > > 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. Netfilter, through policy, allows to modify the box behaviour in different ways that makes it non-RFC compliant. But there is code already code such as nf_skb_is_icmp_unreach() which does not allow to reject a icmp dest unreach with another icmp dest unreach. Also looking at your patch, how does it fix the REJECT case? Maybe you can propose a patch for nf-next as enhancement that can be reviewed to netfilter-devel@vger.kernel.org. Thanks. > > Thanks, > > Sayooj > > > > On Wed, May 27, 2026 at 5:45 PM Eric Dumazet <edumazet@google.com> wrote: > >> > >> On Wed, May 27, 2026 at 2:46 AM Sayooj K Karun <sayooj@aerlync.com> 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@aerlync.com> > >> > --- > >> > 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 > >> > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets 2026-06-01 9:49 ` Pablo Neira Ayuso @ 2026-06-03 6:01 ` Sayooj K Karun 2026-06-03 8:10 ` Pablo Neira Ayuso 0 siblings, 1 reply; 8+ messages in thread From: Sayooj K Karun @ 2026-06-03 6:01 UTC (permalink / raw) To: pablo Cc: aleksander.lobakin, davem, dsahern, edumazet, fw, horms, idosch, kuba, linux-kernel, netdev, pabeni, sayooj, netfilter-devel You are right that netfilter can be configured to make devices behave in non-RFC-compliant ways, so I will drop the "netfilter policy must obey the RFC" framing from my earlier reply. The point I should have made is that is_ineligible() is not a netfilter function. It is the generic gate that icmpv6_send() uses to decide whether the kernel, as an ICMPv6 originator, may emit an error for a given trigger packet, and it is shared by all icmpv6_send() callers. It already enforces RFC 4443 section 2.4(e.1) at exactly this spot, via !(*tp & ICMPV6_INFOMSG_MASK), that is "do not originate an error in response to an ICMPv6 error". My patch adds (e.2) (Redirect) right next to it, the second rule from the same MUST NOT list. So this is not about overriding netfilter policy; it is completing the e.1/e.2 pair at the single point where the kernel decides ICMPv6 error eligibility. On how it fixes the REJECT case: the two IPv6 reject paths differ in who actually frames the ICMPv6 error. The bridge/netdev path, nf_reject_skb_v6_unreach(), builds the packet by hand: it allocates the skb, writes the IPv6 and ICMPv6 headers, copies in the original packet and computes the checksum. Because it does all that itself, it has to carry its own guard, nf_skb_is_icmp6_unreach(), the IPv6 analogue of the nf_skb_is_icmp_unreach() you mention. The L3 path, ip6t_REJECT / nft_reject -> nf_send_unreach6(), never frames a packet of its own. It just calls icmpv6_send() and lets the core ICMPv6 stack build and send the error. is_ineligible() is the gate that core builder consults first, before it allocates or assembles anything, so that is exactly where the e.1 suppression already lives for this path. There is no netfilter-local guard here, and there does not need to be. So the scenario in my commit message is the L3 path: ip6t_REJECT / nft_reject > nf_send_unreach6() > icmpv6_send() / icmp6_send() > is_ineligible() // now returns true for NDISC_REDIRECT > goto out, no packet is ever built or transmitted The patch fixes the REJECT case because the L3 reject hands packet construction to icmp6_send(), and is_ineligible() runs at the top of that builder, before any error skb exists. It is the same spot that already drops e.1 today, so adding e.2 there completes the pair rather than introducing a new override. I also agree there is a gap to close on the netfilter side. The bridge/netdev path never reaches is_ineligible(), and its nf_skb_is_icmp6_unreach() guard currently checks only ICMPV6_DEST_UNREACH, not Redirect, so it is not covered by this patch. I will send a follow-up to netfilter-devel for nf-next extending that guard to also skip Redirect, so both paths behave consistently. Does that split sound right? this fix to is_ineligible() for the L3 path, plus a separate nf-next patch for the bridge/netdev reject guard? Thanks, Sayooj ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets 2026-06-03 6:01 ` [PATCH] " Sayooj K Karun @ 2026-06-03 8:10 ` Pablo Neira Ayuso 0 siblings, 0 replies; 8+ messages in thread From: Pablo Neira Ayuso @ 2026-06-03 8:10 UTC (permalink / raw) To: Sayooj K Karun Cc: aleksander.lobakin, davem, dsahern, edumazet, fw, horms, idosch, kuba, linux-kernel, netdev, pabeni, netfilter-devel On Wed, Jun 03, 2026 at 11:31:12AM +0530, Sayooj K Karun wrote: > You are right that netfilter can be configured to make devices behave in > non-RFC-compliant ways, so I will drop the "netfilter policy must obey > the RFC" framing from my earlier reply. > > The point I should have made is that is_ineligible() is not a netfilter > function. It is the generic gate that icmpv6_send() uses to decide > whether the kernel, as an ICMPv6 originator, may emit an error for a > given trigger packet, and it is shared by all icmpv6_send() callers. It > already enforces RFC 4443 section 2.4(e.1) at exactly this spot, via > !(*tp & ICMPV6_INFOMSG_MASK), that is "do not originate an error in > response to an ICMPv6 error". My patch adds (e.2) (Redirect) right next > to it, the second rule from the same MUST NOT list. So this is not > about overriding netfilter policy; it is completing the e.1/e.2 pair at > the single point where the kernel decides ICMPv6 error eligibility. > > On how it fixes the REJECT case: the two IPv6 reject paths differ in who > actually frames the ICMPv6 error. The bridge/netdev path, > nf_reject_skb_v6_unreach(), builds the packet by hand: it allocates the > skb, writes the IPv6 and ICMPv6 headers, copies in the original packet > and computes the checksum. Because it does all that itself, it has to > carry its own guard, nf_skb_is_icmp6_unreach(), the IPv6 analogue of the > nf_skb_is_icmp_unreach() you mention. Yes, because bridge/netdev path cannot assume the IP stack comes into play, so it needs a custom function to build the packet to reject the traffic. > The L3 path, ip6t_REJECT / nft_reject -> nf_send_unreach6(), never frames > a packet of its own. It just calls icmpv6_send() and lets the core > ICMPv6 stack build and send the error. is_ineligible() is the gate that > core builder consults first, before it allocates or assembles anything, > so that is exactly where the e.1 suppression already lives for this path. > There is no netfilter-local guard here, and there does not need to be. > > So the scenario in my commit message is the L3 path: > > ip6t_REJECT / nft_reject > > nf_send_unreach6() > > icmpv6_send() / icmp6_send() > > is_ineligible() // now returns true for NDISC_REDIRECT > > goto out, no packet is ever built or transmitted > > The patch fixes the REJECT case because the L3 reject hands packet > construction to icmp6_send(), and is_ineligible() runs at the top of > that builder, before any error skb exists. It is the same spot that > already drops e.1 today, so adding e.2 there completes the pair rather > than introducing a new override. > > I also agree there is a gap to close on the netfilter side. The > bridge/netdev path never reaches is_ineligible(), and its > nf_skb_is_icmp6_unreach() guard currently checks only > ICMPV6_DEST_UNREACH, not Redirect, so it is not covered by this patch. I > will send a follow-up to netfilter-devel for nf-next extending that > guard to also skip Redirect, so both paths behave consistently. Yes, nf-next is the target for this for review. > Does that split sound right? this fix to is_ineligible() for the L3 > path, plus a separate nf-next patch for the bridge/netdev reject guard? Maybe, I did not look in depth with details, but posting a patch upfront to see how things look like make it easier for us, even if more changes are later requested on it. Thanks. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-03 8:10 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-26 10:16 [PATCH] net/ipv6: icmp: fix is_ineligible() to block errors for Redirect packets Sayooj K Karun
2026-05-26 15:40 ` Alexander Lobakin
2026-05-27 9:46 ` [PATCH v2 net] " Sayooj K Karun
2026-05-27 12:15 ` Eric Dumazet
[not found] ` <CAFdLOz67Sxne4yXi3hB7Nf0eX=SzE9-P1phG4OzYfrgjQvmcGw@mail.gmail.com>
2026-06-01 7:32 ` Eric Dumazet
2026-06-01 9:49 ` Pablo Neira Ayuso
2026-06-03 6:01 ` [PATCH] " Sayooj K Karun
2026-06-03 8:10 ` Pablo Neira Ayuso
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox