* [PATCH net v2 0/2] ipv6: honor prefix-length suppression in raw FIB lookups
@ 2026-11-15 11:44 Leonard Lausen
2026-11-15 11:44 ` [PATCH net v2 1/2] " Leonard Lausen
2026-11-15 11:44 ` [PATCH net v2 2/2] selftests: netfilter: cover IPv6 FIB prefix suppression Leonard Lausen
0 siblings, 2 replies; 4+ messages in thread
From: Leonard Lausen @ 2026-11-15 11:44 UTC (permalink / raw)
To: netdev
Cc: leonard, David Ahern, Ido Schimmel, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, Shuah Khan,
Daniel Borkmann, netfilter-devel, coreteam, linux-kselftest,
linux-kernel
Following Sashiko's automated review, v2 is limited to
suppress_prefixlength. The review identified that v1 evaluated
suppress_ifgroup before final multipath selection; I reproduced the
resulting disagreement between raw and ordinary lookups. Interface-group
suppression needs separate work and is left unchanged in this revision.
Prefix suppression is sufficient for the reported VPN failure: nftables
can otherwise return a main-table default that an ordinary lookup
suppresses before continuing to the VPN table. Strict reverse-path
filtering then drops replies arriving through the VPN.
Patch 1 adds a raw prefix-length check while preserving RTF_REJECT results
and leaving dst-based handling unchanged. Patch 2 extends nft_fib.sh with
six checks using its existing topology and counter helper.
Changes since v1:
- Drop raw suppress_ifgroup support and its tests. Table lookup can return
a different nexthop from the final multipath selection used by ordinary
routing and BPF, so suppression cannot safely use that initial device.
- Install the router's logging RPF rules after the suppression checks and
policy-rule cleanup. This prevents neighbour advertisements from being
logged and dropped during intentionally different reverse-route states.
- Add a blackhole nexthop-object case, which distinguishes rejection flags
in the lookup result from those in the FIB entry.
V1: https://lore.kernel.org/netdev/20261110131638.15004-3-leonard@lausen.nl/
Tested on the rebuilt v7.4-rc2-based kernel: all six added checks pass,
and the full nft_fib.sh run exits 0. A run with an eight-second delay also
passes, allowing neighbour discovery to complete. Separate probes confirm
that reject routes remain terminal and raw interface-group suppression
is unchanged.
OpenAI Codex assisted with investigation, reproduction, implementation,
tests and descriptions. The prompts asked it to investigate the
Wireguard VPN failure.
Leonard Lausen (2):
ipv6: honor prefix-length suppression in raw FIB lookups
selftests: netfilter: cover IPv6 FIB prefix suppression
net/ipv6/fib6_rules.c | 9 ++-
.../selftests/net/netfilter/nft_fib.sh | 63 ++++++++++++++++++-
2 files changed, 70 insertions(+), 2 deletions(-)
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net v2 1/2] ipv6: honor prefix-length suppression in raw FIB lookups
2026-11-15 11:44 [PATCH net v2 0/2] ipv6: honor prefix-length suppression in raw FIB lookups Leonard Lausen
@ 2026-11-15 11:44 ` Leonard Lausen
2026-11-15 15:46 ` Ido Schimmel
2026-11-15 11:44 ` [PATCH net v2 2/2] selftests: netfilter: cover IPv6 FIB prefix suppression Leonard Lausen
1 sibling, 1 reply; 4+ messages in thread
From: Leonard Lausen @ 2026-11-15 11:44 UTC (permalink / raw)
To: netdev
Cc: leonard, David Ahern, Ido Schimmel, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, Shuah Khan,
Daniel Borkmann, netfilter-devel, coreteam, linux-kselftest,
linux-kernel
IPv6 raw FIB lookups populate f6i and nh in fib6_result, but not rt6.
fib6_rule_suppress() only examines rt6, so fib6_lookup() ignores
suppress_prefixlength.
With a policy rule that suppresses the main-table default before looking
in a VPN table, an ordinary route lookup selects the VPN while an nftables
FIB lookup returns the suppressed default. Strict reverse-path filtering
can then drop replies arriving over the VPN.
For raw lookups, evaluate suppress_prefixlength against the FIB entry's
destination prefix length. Preserve results marked RTF_REJECT: raw lookups
can return zero with a rejection encoded in the result, while dst-based
lookups return an error and skip suppression. Check the result flags so
blackhole nexthop objects are covered as well.
Keep the dst-based path unchanged. Raw suppress_ifgroup handling also
remains unchanged because the table lookup's nexthop need not be the
final multipath selection.
Fixes: 138118ec96cb ("net/ipv6: Add fib6_lookup")
Link: https://github.com/firewalld/firewalld/issues/1600
Assisted-by: LLM
Signed-off-by: Leonard Lausen <leonard@lausen.nl>
---
Investigated with AI assistance after VPN failures. Reproduced on
unmodified v7.4-rc2 and verified with nft_fib.sh on the rebuilt kernel.
net/ipv6/fib6_rules.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index 0ffd7b47a046..ceb4180bcf2f 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -283,9 +283,16 @@ INDIRECT_CALLABLE_SCOPE bool fib6_rule_suppress(struct fib_rule *rule,
struct fib_lookup_arg *arg)
{
struct fib6_result *res = arg->result;
- struct rt6_info *rt = res->rt6;
+ struct rt6_info *rt;
struct net_device *dev = NULL;
+ if (arg->lookup_ptr == fib6_table_lookup) {
+ /* Raw lookups report reject routes without an error. */
+ return !(res->fib6_flags & RTF_REJECT) &&
+ res->f6i->fib6_dst.plen <= rule->suppress_prefixlen;
+ }
+
+ rt = res->rt6;
if (!rt)
return false;
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net v2 2/2] selftests: netfilter: cover IPv6 FIB prefix suppression
2026-11-15 11:44 [PATCH net v2 0/2] ipv6: honor prefix-length suppression in raw FIB lookups Leonard Lausen
2026-11-15 11:44 ` [PATCH net v2 1/2] " Leonard Lausen
@ 2026-11-15 11:44 ` Leonard Lausen
1 sibling, 0 replies; 4+ messages in thread
From: Leonard Lausen @ 2026-11-15 11:44 UTC (permalink / raw)
To: netdev
Cc: leonard, David Ahern, Ido Schimmel, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, Shuah Khan,
Daniel Borkmann, netfilter-devel, coreteam, linux-kselftest,
linux-kernel
Extend nft_fib.sh using its existing namespaces and counter helper to
check that suppress_prefixlength 0 suppresses a default route while
retaining a more-specific route. Check that blackhole, unreachable and
prohibit routes remain terminal despite a usable route in a later table.
Include a blackhole nexthop object to distinguish result reject flags
from flags on the FIB entry.
Require a positive packet counter for the expected nftables FIB result
and compare it with an ordinary route lookup. Install the router's
logging RPF rules after these checks and their policy-rule cleanup, so
neighbour advertisements cannot be logged and dropped while intentionally
different reverse routes are installed.
Assisted-by: LLM
Signed-off-by: Leonard Lausen <leonard@lausen.nl>
---
.../selftests/net/netfilter/nft_fib.sh | 63 ++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/netfilter/nft_fib.sh b/tools/testing/selftests/net/netfilter/nft_fib.sh
index 04544905c216..65d4687622e2 100755
--- a/tools/testing/selftests/net/netfilter/nft_fib.sh
+++ b/tools/testing/selftests/net/netfilter/nft_fib.sh
@@ -202,7 +202,38 @@ check_fib_counter() {
return 0
}
-load_ruleset "$nsrouter"
+# Count probes matching the expected FIB result, then drop them.
+test_fib_suppress() {
+ local label="$1" expected="$2"
+ local result="${3:-oifname}" err=0
+
+ ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF || return 1
+flush table ip6 filter
+table ip6 filter {
+ chain prerouting {
+ type filter hook prerouting priority -1;
+ ip6 saddr dead:1::99 icmpv6 type echo-request \
+ fib saddr . mark $result "$expected" counter drop
+ }
+}
+EOF
+ ip netns exec "$ns1" ping -6 -c 1 -W 0.1 -q dead:1::1 > /dev/null 2>&1
+ if [ "$result" = type ]; then
+ ip -net "$nsrouter" -6 route get dead:1::99 from dead:1::1 > /dev/null 2>&1 && err=1
+ else
+ ip -net "$nsrouter" -6 route get dead:1::99 from dead:1::1 |
+ grep -q "dev $expected " || err=1
+ fi
+ if check_fib_type_counter_family ip6 1 "$nsrouter" prerouting "$result" "$label" &&
+ [ "$err" -eq 0 ]; then
+ echo "PASS: $label"
+ else
+ echo "FAIL: $label"
+ ret=1
+ return 1
+ fi
+}
+
load_ruleset "$ns1"
load_ruleset "$ns2"
@@ -724,6 +755,36 @@ test_fib_vrf()
check_fib_veth_vrf_type "fib expression address types match (iif in vrf)"
}
+# Test prefix suppression before installing the router's logging RPF rules.
+# Neighbour discovery must remain usable even when reverse routes differ.
+ip -net "$nsrouter" -6 route add table 128 default dev veth1 || exit 1
+ip -net "$nsrouter" -6 route add table 129 dead:1::/64 dev veth0 || exit 1
+ip -net "$nsrouter" -6 rule add pref 100 to dead:1::99 table 128 suppress_prefixlength 0 || exit 1
+ip -net "$nsrouter" -6 rule add pref 101 to dead:1::99 table 129 || exit 1
+ip netns exec "$nsrouter" nft add table ip6 filter || exit 1
+
+test_fib_suppress "suppress default route" veth0 || ret=1
+ip -net "$nsrouter" -6 route add table 128 dead:1::/64 dev veth1 || exit 1
+test_fib_suppress "retain non-default route" veth1 || ret=1
+ip -net "$nsrouter" -6 route del table 128 dead:1::/64 dev veth1 || exit 1
+
+for type in blackhole unreachable prohibit; do
+ ip -net "$nsrouter" -6 route replace "$type" default table 128 || exit 1
+ test_fib_suppress "retain $type" "$type" type || ret=1
+done
+
+ip -net "$nsrouter" -6 nexthop add id 1 blackhole || exit 1
+ip -net "$nsrouter" -6 route replace default nhid 1 table 128 || exit 1
+test_fib_suppress "retain blackhole nexthop" blackhole type || ret=1
+
+ip -net "$nsrouter" -6 rule del pref 100
+ip -net "$nsrouter" -6 rule del pref 101
+ip -net "$nsrouter" -6 route del table 128 default
+ip -net "$nsrouter" -6 route flush table 129
+ip -net "$nsrouter" -6 nexthop del id 1
+ip netns exec "$nsrouter" nft delete table ip6 filter
+load_ruleset "$nsrouter"
+
ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net v2 1/2] ipv6: honor prefix-length suppression in raw FIB lookups
2026-11-15 11:44 ` [PATCH net v2 1/2] " Leonard Lausen
@ 2026-11-15 15:46 ` Ido Schimmel
0 siblings, 0 replies; 4+ messages in thread
From: Ido Schimmel @ 2026-11-15 15:46 UTC (permalink / raw)
To: Leonard Lausen
Cc: netdev, David Ahern, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Pablo Neira Ayuso,
Florian Westphal, Phil Sutter, Shuah Khan, Daniel Borkmann,
netfilter-devel, coreteam, linux-kselftest, linux-kernel
On Sun, Nov 15, 2026 at 11:44:32AM +0000, Leonard Lausen wrote:
> IPv6 raw FIB lookups populate f6i and nh in fib6_result, but not rt6.
> fib6_rule_suppress() only examines rt6, so fib6_lookup() ignores
> suppress_prefixlength.
>
> With a policy rule that suppresses the main-table default before looking
> in a VPN table, an ordinary route lookup selects the VPN while an nftables
> FIB lookup returns the suppressed default. Strict reverse-path filtering
> can then drop replies arriving over the VPN.
>
> For raw lookups, evaluate suppress_prefixlength against the FIB entry's
> destination prefix length. Preserve results marked RTF_REJECT: raw lookups
> can return zero with a rejection encoded in the result, while dst-based
> lookups return an error and skip suppression. Check the result flags so
> blackhole nexthop objects are covered as well.
>
> Keep the dst-based path unchanged. Raw suppress_ifgroup handling also
> remains unchanged because the table lookup's nexthop need not be the
> final multipath selection.
Never heard of "IPv6 raw FIB lookups". This commit message [1] is easier
to understand.
BTW, I think that fib6_rule_suppress() should always work on the FIB
entry ('rt->from') instead of the dst entry. Currently, if the default
route has an exception for the destination (e.g., via PMTU), then the
default route will not be suppressed since 'rt->rt6i_dst.plen' is 128.
I will add it to my TODO list. This patch doesn't need to fix it.
>
> Fixes: 138118ec96cb ("net/ipv6: Add fib6_lookup")
Should be:
Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup")
The tag should point to the commit that introduced the regression.
> Link: https://github.com/firewalld/firewalld/issues/1600
> Assisted-by: LLM
Please remove this tag. See commit 1e035297ffda ("docs: remove
Assisted-by references").
> Signed-off-by: Leonard Lausen <leonard@lausen.nl>
> ---
> Investigated with AI assistance after VPN failures. Reproduced on
> unmodified v7.4-rc2 and verified with nft_fib.sh on the rebuilt kernel.
>
> net/ipv6/fib6_rules.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
> index 0ffd7b47a046..ceb4180bcf2f 100644
> --- a/net/ipv6/fib6_rules.c
> +++ b/net/ipv6/fib6_rules.c
> @@ -283,9 +283,16 @@ INDIRECT_CALLABLE_SCOPE bool fib6_rule_suppress(struct fib_rule *rule,
> struct fib_lookup_arg *arg)
> {
> struct fib6_result *res = arg->result;
> - struct rt6_info *rt = res->rt6;
> + struct rt6_info *rt;
Unnecessary change, please drop. It also breaks reverse xmas tree:
https://docs.kernel.org/next/process/maintainer-netdev.html#local-variable-ordering-reverse-xmas-tree-rcs
> struct net_device *dev = NULL;
>
> + if (arg->lookup_ptr == fib6_table_lookup) {
> + /* Raw lookups report reject routes without an error. */
/* fib6_table_lookup() returns 0 for reject routes. */
> + return !(res->fib6_flags & RTF_REJECT) &&
> + res->f6i->fib6_dst.plen <= rule->suppress_prefixlen;
> + }
> +
> + rt = res->rt6;
> if (!rt)
> return false;
>
> --
> 2.55.0
>
[1]
ipv6: Honor suppress_prefixlength in fib6_lookup()
When custom FIB rules are configured, fib6_lookup() performs the lookup
via fib6_rule_action_alt() which calls fib6_table_lookup() and only
populates the f6i, nh, fib6_flags and fib6_type fields of the FIB
result. However, fib6_rule_suppress() only examines the rt6 field, which
is only populated when the lookup is performed via one of the callers of
fib6_rule_lookup() such as ip6_route_output(). As a result,
fib6_lookup() ignores suppress_prefixlength.
This was not a problem for nftables until commit 1c32b24c234b
("netfilter: nft_fib_ipv6: switch to fib6_lookup") switched the FIB
expression from ip6_route_lookup() / nf_ip6_route() to fib6_lookup().
Since then, with a rule that suppresses the default route in the main
table before looking up a VPN table (as commonly configured for
WireGuard), the FIB expression returns the suppressed default route
instead of the VPN route. Strict reverse path filtering then drops
replies received over the VPN.
Fix by evaluating suppress_prefixlength against the prefix length of
the FIB entry when the lookup is performed via fib6_lookup().
fib_rules_lookup() only invokes the suppress callback when the rule
action returns 0. Unlike __fib6_rule_action(), fib6_rule_action_alt()
returns 0 for reject routes and instead sets RTF_REJECT in the result.
Do not suppress such routes so that they continue to terminate the
lookup, as with ip6_route_output() and IPv4. Check the flags in the
result rather than in the FIB entry so that blackhole nexthop objects
are also covered.
suppress_ifgroup is not handled since fib6_table_lookup() does not
perform multipath selection and the nexthop it returns might not be the
one that is eventually used.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-15 15:46 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-15 11:44 [PATCH net v2 0/2] ipv6: honor prefix-length suppression in raw FIB lookups Leonard Lausen
2026-11-15 11:44 ` [PATCH net v2 1/2] " Leonard Lausen
2026-11-15 15:46 ` Ido Schimmel
2026-11-15 11:44 ` [PATCH net v2 2/2] selftests: netfilter: cover IPv6 FIB prefix suppression Leonard Lausen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®