diff options
| author | Gennaro Cimmino | 2026-10-04 18:39:27 +0000 |
|---|---|---|
| committer | Markus Stockhausen | 2026-10-04 19:46:56 +0000 |
| commit | f7950775283a36b6bd8de40fc8f14813d4f320b1 (patch) | |
| tree | 8c080aa2fc9e4f9d481f0159d83ee16e53f7f0fa | |
| parent | 5a7b0432844f40a3b352f5d65d3913c959e29190 (diff) | |
A rule given an explicit dscp selector keeps it in the family's own
part of the rule, with a flag that tells dscp 0 from no selector at
all. fib4_rule_default() and fib6_rule_default() only test the dscp
value, so "dscp 0 lookup main" passed for an unrestricted main rule:
packets with any other dscp go on to the next rules in the kernel, and
the hardware forwarded them by main all the same.
Only the family's own callbacks see that part of the rule, and its fill
callback reports an explicit selector as FRA_DSCP. Run it into a
scratch buffer and count a local or main rule with FRA_DSCP, or one
that cannot be read out, as restricted.
Tested on a Hasivo S1100W-8XGT-SE (RTL9303) with L3 offload enabled:
a prefix routed in main via a host on another VLAN, blackholed in
table 100, with "dscp 0 lookup main" ahead of a rule sending the
sender's subnet to table 100. Echoes with dscp 8: without this change
the row keeps forwarding and all 5 reach the host, with it the row
traps and none do, for IPv4 and IPv6. With it, echoes with dscp 0 still
reach the host, routed by the kernel through main.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25618
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
| -rw-r--r-- | target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c index f90ed89d1a..f0ea0f649a 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/l3.c @@ -2580,20 +2580,47 @@ static int otto_l3_fib_del_v6(struct otto_l3_ctrl *ctrl, struct fib6_entry_notif return 0; } -static bool otto_l3_rule_default(const struct fib_rule *rule, int family) +/* A dscp selector the rule was given explicitly, 0 included, is kept in the + * family's own part of the rule, which only the family's callbacks see; its + * fill callback reports one as FRA_DSCP. A rule that cannot be read out counts + * as having one. + */ +static bool otto_l3_rule_dscp(struct fib_rules_ops *ops, struct fib_rule *rule) { + struct fib_rule_hdr *frh; + struct sk_buff *skb; + bool dscp = true; + + skb = alloc_skb(sizeof(*frh) + ops->nlmsg_payload(rule), GFP_KERNEL); + if (!skb) + return true; + + frh = skb_put_zero(skb, sizeof(*frh)); + if (!ops->fill(rule, skb, frh)) + dscp = nla_find((struct nlattr *)(frh + 1), skb->len - sizeof(*frh), FRA_DSCP); + + consume_skb(skb); + + return dscp; +} + +static bool otto_l3_rule_default(struct fib_rules_ops *ops, struct fib_rule *rule, + int family) +{ + bool def = false; + /* The kernel's own test leaves the protocol and the mark mask out */ if (rule->ip_proto || rule->mark_mask) return false; #if IS_ENABLED(CONFIG_IP_MULTIPLE_TABLES) if (family == AF_INET) - return fib4_rule_default(rule); + def = fib4_rule_default(rule); #endif #if IS_REACHABLE(CONFIG_IPV6) && IS_ENABLED(CONFIG_IPV6_MULTIPLE_TABLES) if (family == AF_INET6) - return fib6_rule_default(rule); + def = fib6_rule_default(rule); #endif - return false; + return def && !otto_l3_rule_dscp(ops, rule); } /* The switch looks a destination up in one table, so it forwards the way the @@ -2631,7 +2658,7 @@ static bool otto_l3_rules_allow(int family) break; } - if (otto_l3_rule_default(rule, family)) { + if (otto_l3_rule_default(ops, rule, family)) { if (rule->table == RT_TABLE_MAIN) { main_seen = true; main_pref = rule->pref; |