Merge branch 'fib_rules-fix-iif-oif-matching-on-l3-master-device'

Ido Schimmel says:

====================
fib_rules: Fix iif / oif matching on L3 master device

Patch #1 fixes a recently reported regression regarding FIB rules that
match on iif / oif being a VRF device.

Patch #2 adds test cases to the FIB rules selftest.
====================

Link: https://patch.msgid.link/20250414172022.242991-1-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-04-15 17:54:58 -07:00
commit 277cc13a5d
6 changed files with 107 additions and 9 deletions

View File

@ -45,6 +45,8 @@ struct fib_rule {
struct fib_rule_port_range dport_range;
u16 sport_mask;
u16 dport_mask;
u8 iif_is_l3_master;
u8 oif_is_l3_master;
struct rcu_head rcu;
};

View File

@ -38,6 +38,7 @@ struct flowi_common {
__u8 flowic_flags;
#define FLOWI_FLAG_ANYSRC 0x01
#define FLOWI_FLAG_KNOWN_NH 0x02
#define FLOWI_FLAG_L3MDEV_OIF 0x04
__u32 flowic_secid;
kuid_t flowic_uid;
__u32 flowic_multipath_hash;

View File

@ -59,6 +59,20 @@ int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type, struct net *net,
int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
struct fib_lookup_arg *arg);
static inline
bool l3mdev_fib_rule_iif_match(const struct flowi *fl, int iifindex)
{
return !(fl->flowi_flags & FLOWI_FLAG_L3MDEV_OIF) &&
fl->flowi_l3mdev == iifindex;
}
static inline
bool l3mdev_fib_rule_oif_match(const struct flowi *fl, int oifindex)
{
return fl->flowi_flags & FLOWI_FLAG_L3MDEV_OIF &&
fl->flowi_l3mdev == oifindex;
}
void l3mdev_update_flow(struct net *net, struct flowi *fl);
int l3mdev_master_ifindex_rcu(const struct net_device *dev);
@ -327,6 +341,19 @@ int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
{
return 1;
}
static inline
bool l3mdev_fib_rule_iif_match(const struct flowi *fl, int iifindex)
{
return false;
}
static inline
bool l3mdev_fib_rule_oif_match(const struct flowi *fl, int oifindex)
{
return false;
}
static inline
void l3mdev_update_flow(struct net *net, struct flowi *fl)
{

View File

@ -257,6 +257,24 @@ static int nla_put_port_range(struct sk_buff *skb, int attrtype,
return nla_put(skb, attrtype, sizeof(*range), range);
}
static bool fib_rule_iif_match(const struct fib_rule *rule, int iifindex,
const struct flowi *fl)
{
u8 iif_is_l3_master = READ_ONCE(rule->iif_is_l3_master);
return iif_is_l3_master ? l3mdev_fib_rule_iif_match(fl, iifindex) :
fl->flowi_iif == iifindex;
}
static bool fib_rule_oif_match(const struct fib_rule *rule, int oifindex,
const struct flowi *fl)
{
u8 oif_is_l3_master = READ_ONCE(rule->oif_is_l3_master);
return oif_is_l3_master ? l3mdev_fib_rule_oif_match(fl, oifindex) :
fl->flowi_oif == oifindex;
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
struct flowi *fl, int flags,
struct fib_lookup_arg *arg)
@ -264,11 +282,11 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
int iifindex, oifindex, ret = 0;
iifindex = READ_ONCE(rule->iifindex);
if (iifindex && (iifindex != fl->flowi_iif))
if (iifindex && !fib_rule_iif_match(rule, iifindex, fl))
goto out;
oifindex = READ_ONCE(rule->oifindex);
if (oifindex && (oifindex != fl->flowi_oif))
if (oifindex && !fib_rule_oif_match(rule, oifindex, fl))
goto out;
if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask)
@ -736,16 +754,20 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
struct net_device *dev;
dev = __dev_get_by_name(nlrule->fr_net, nlrule->iifname);
if (dev)
if (dev) {
nlrule->iifindex = dev->ifindex;
nlrule->iif_is_l3_master = netif_is_l3_master(dev);
}
}
if (tb[FRA_OIFNAME]) {
struct net_device *dev;
dev = __dev_get_by_name(nlrule->fr_net, nlrule->oifname);
if (dev)
if (dev) {
nlrule->oifindex = dev->ifindex;
nlrule->oif_is_l3_master = netif_is_l3_master(dev);
}
}
return 0;
@ -1336,11 +1358,17 @@ static void attach_rules(struct list_head *rules, struct net_device *dev)
list_for_each_entry(rule, rules, list) {
if (rule->iifindex == -1 &&
strcmp(dev->name, rule->iifname) == 0)
strcmp(dev->name, rule->iifname) == 0) {
WRITE_ONCE(rule->iifindex, dev->ifindex);
WRITE_ONCE(rule->iif_is_l3_master,
netif_is_l3_master(dev));
}
if (rule->oifindex == -1 &&
strcmp(dev->name, rule->oifname) == 0)
strcmp(dev->name, rule->oifname) == 0) {
WRITE_ONCE(rule->oifindex, dev->ifindex);
WRITE_ONCE(rule->oif_is_l3_master,
netif_is_l3_master(dev));
}
}
}
@ -1349,10 +1377,14 @@ static void detach_rules(struct list_head *rules, struct net_device *dev)
struct fib_rule *rule;
list_for_each_entry(rule, rules, list) {
if (rule->iifindex == dev->ifindex)
if (rule->iifindex == dev->ifindex) {
WRITE_ONCE(rule->iifindex, -1);
if (rule->oifindex == dev->ifindex)
WRITE_ONCE(rule->iif_is_l3_master, false);
}
if (rule->oifindex == dev->ifindex) {
WRITE_ONCE(rule->oifindex, -1);
WRITE_ONCE(rule->oif_is_l3_master, false);
}
}
}

View File

@ -277,8 +277,10 @@ void l3mdev_update_flow(struct net *net, struct flowi *fl)
if (fl->flowi_oif) {
dev = dev_get_by_index_rcu(net, fl->flowi_oif);
if (dev) {
if (!fl->flowi_l3mdev)
if (!fl->flowi_l3mdev) {
fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev);
fl->flowi_flags |= FLOWI_FLAG_L3MDEV_OIF;
}
/* oif set to L3mdev directs lookup to its table;
* reset to avoid oif match in fib_lookup

View File

@ -359,6 +359,23 @@ fib_rule6_test()
"$getnomatch" "iif flowlabel masked redirect to table" \
"iif flowlabel masked no redirect to table"
fi
$IP link show dev $DEV | grep -q vrf0
if [ $? -eq 0 ]; then
match="oif vrf0"
getmatch="oif $DEV"
getnomatch="oif lo"
fib_rule6_test_match_n_redirect "$match" "$getmatch" \
"$getnomatch" "VRF oif redirect to table" \
"VRF oif no redirect to table"
match="from $SRC_IP6 iif vrf0"
getmatch="from $SRC_IP6 iif $DEV"
getnomatch="from $SRC_IP6 iif lo"
fib_rule6_test_match_n_redirect "$match" "$getmatch" \
"$getnomatch" "VRF iif redirect to table" \
"VRF iif no redirect to table"
fi
}
fib_rule6_vrf_test()
@ -635,6 +652,23 @@ fib_rule4_test()
"$getnomatch" "iif dscp masked redirect to table" \
"iif dscp masked no redirect to table"
fi
$IP link show dev $DEV | grep -q vrf0
if [ $? -eq 0 ]; then
match="oif vrf0"
getmatch="oif $DEV"
getnomatch="oif lo"
fib_rule4_test_match_n_redirect "$match" "$getmatch" \
"$getnomatch" "VRF oif redirect to table" \
"VRF oif no redirect to table"
match="from $SRC_IP iif vrf0"
getmatch="from $SRC_IP iif $DEV"
getnomatch="from $SRC_IP iif lo"
fib_rule4_test_match_n_redirect "$match" "$getmatch" \
"$getnomatch" "VRF iif redirect to table" \
"VRF iif no redirect to table"
fi
}
fib_rule4_vrf_test()