Skip to content

Commit

Permalink
Merge branch 'fib_rules-fix-iif-oif-matching-on-l3-master-device'
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jakub Kicinski committed Apr 16, 2025
2 parents 12f2d03 + f9c8759 commit 277cc13
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 9 deletions.
2 changes: 2 additions & 0 deletions include/net/fib_rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
1 change: 1 addition & 0 deletions include/net/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions include/net/l3mdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down
48 changes: 40 additions & 8 deletions net/core/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,18 +257,36 @@ 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)
{
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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
}
}

Expand All @@ -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);
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion net/l3mdev/l3mdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions tools/testing/selftests/net/fib_rule_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 277cc13

Please sign in to comment.