Skip to content

Commit 264b5f8

Browse files
committed
net: fib_rules: Fix iif / oif matching on L3 master device
JIRA: https://issues.redhat.com/browse/RHEL-84551 Upstream Status: linux.git Conflicts:\ - Context differences due to missing upstream commit da76659 ("net: fib_rules: Add port mask support"). - Chunk in fib_nl2rule_rtnl was moved to fib_nl2rule due to missing upstream commit 8b49877 ("net: fib_rules: Split fib_nl2rule()."). commit 2d300ce Author: Ido Schimmel <idosch@nvidia.com> Date: Mon Apr 14 20:20:21 2025 +0300 net: fib_rules: Fix iif / oif matching on L3 master device Before commit 40867d7 ("net: Add l3mdev index to flow struct and avoid oif reset for port devices") it was possible to use FIB rules to match on a L3 domain. This was done by having a FIB rule match on iif / oif being a L3 master device. It worked because prior to the FIB rule lookup the iif / oif fields in the flow structure were reset to the index of the L3 master device to which the input / output device was enslaved to. The above scheme made it impossible to match on the original input / output device. Therefore, cited commit stopped overwriting the iif / oif fields in the flow structure and instead stored the index of the enslaving L3 master device in a new field ('flowi_l3mdev') in the flow structure. While the change enabled new use cases, it broke the original use case of matching on a L3 domain. Fix this by interpreting the iif / oif matching on a L3 master device as a match against the L3 domain. In other words, if the iif / oif in the FIB rule points to a L3 master device, compare the provided index against 'flowi_l3mdev' rather than 'flowi_{i,o}if'. Before cited commit, a FIB rule that matched on 'iif vrf1' would only match incoming traffic from devices enslaved to 'vrf1'. With the proposed change (i.e., comparing against 'flowi_l3mdev'), the rule would also match traffic originating from a socket bound to 'vrf1'. Avoid that by adding a new flow flag ('FLOWI_FLAG_L3MDEV_OIF') that indicates if the L3 domain was derived from the output interface or the input interface (when not set) and take this flag into account when evaluating the FIB rule against the flow structure. Avoid unnecessary checks in the data path by detecting that a rule matches on a L3 master device when the rule is installed and marking it as such. Tested using the following script [1]. Output before 40867d7 (v5.4.291): default dev dummy1 table 100 scope link default dev dummy1 table 200 scope link Output after 40867d7: default dev dummy1 table 300 scope link default dev dummy1 table 300 scope link Output with this patch: default dev dummy1 table 100 scope link default dev dummy1 table 200 scope link [1] #!/bin/bash ip link add name vrf1 up type vrf table 10 ip link add name dummy1 up master vrf1 type dummy sysctl -wq net.ipv4.conf.all.forwarding=1 sysctl -wq net.ipv4.conf.all.rp_filter=0 ip route add table 100 default dev dummy1 ip route add table 200 default dev dummy1 ip route add table 300 default dev dummy1 ip rule add prio 0 oif vrf1 table 100 ip rule add prio 1 iif vrf1 table 200 ip rule add prio 2 table 300 ip route get 192.0.2.1 oif dummy1 fibmatch ip route get 192.0.2.1 iif dummy1 from 198.51.100.1 fibmatch Fixes: 40867d7 ("net: Add l3mdev index to flow struct and avoid oif reset for port devices") Reported-by: hanhuihui <hanhuihui5@huawei.com> Closes: https://lore.kernel.org/netdev/ec671c4f821a4d63904d0da15d604b75@huawei.com/ Signed-off-by: Ido Schimmel <idosch@nvidia.com> Acked-by: David Ahern <dsahern@kernel.org> Link: https://patch.msgid.link/20250414172022.242991-2-idosch@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Antoine Tenart <atenart@redhat.com>
1 parent 05a94d4 commit 264b5f8

File tree

5 files changed

+73
-9
lines changed

5 files changed

+73
-9
lines changed

include/net/fib_rules.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ struct fib_rule {
4343
struct fib_kuid_range uid_range;
4444
struct fib_rule_port_range sport_range;
4545
struct fib_rule_port_range dport_range;
46+
u8 iif_is_l3_master;
47+
u8 oif_is_l3_master;
4648
struct rcu_head rcu;
4749
};
4850

include/net/flow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct flowi_common {
3838
__u8 flowic_flags;
3939
#define FLOWI_FLAG_ANYSRC 0x01
4040
#define FLOWI_FLAG_KNOWN_NH 0x02
41+
#define FLOWI_FLAG_L3MDEV_OIF 0x04
4142
__u32 flowic_secid;
4243
kuid_t flowic_uid;
4344
__u32 flowic_multipath_hash;

include/net/l3mdev.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type, struct net *net,
5959
int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
6060
struct fib_lookup_arg *arg);
6161

62+
static inline
63+
bool l3mdev_fib_rule_iif_match(const struct flowi *fl, int iifindex)
64+
{
65+
return !(fl->flowi_flags & FLOWI_FLAG_L3MDEV_OIF) &&
66+
fl->flowi_l3mdev == iifindex;
67+
}
68+
69+
static inline
70+
bool l3mdev_fib_rule_oif_match(const struct flowi *fl, int oifindex)
71+
{
72+
return fl->flowi_flags & FLOWI_FLAG_L3MDEV_OIF &&
73+
fl->flowi_l3mdev == oifindex;
74+
}
75+
6276
void l3mdev_update_flow(struct net *net, struct flowi *fl);
6377

6478
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,
327341
{
328342
return 1;
329343
}
344+
345+
static inline
346+
bool l3mdev_fib_rule_iif_match(const struct flowi *fl, int iifindex)
347+
{
348+
return false;
349+
}
350+
351+
static inline
352+
bool l3mdev_fib_rule_oif_match(const struct flowi *fl, int oifindex)
353+
{
354+
return false;
355+
}
356+
330357
static inline
331358
void l3mdev_update_flow(struct net *net, struct flowi *fl)
332359
{

net/core/fib_rules.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -256,18 +256,36 @@ static int nla_put_port_range(struct sk_buff *skb, int attrtype,
256256
return nla_put(skb, attrtype, sizeof(*range), range);
257257
}
258258

259+
static bool fib_rule_iif_match(const struct fib_rule *rule, int iifindex,
260+
const struct flowi *fl)
261+
{
262+
u8 iif_is_l3_master = READ_ONCE(rule->iif_is_l3_master);
263+
264+
return iif_is_l3_master ? l3mdev_fib_rule_iif_match(fl, iifindex) :
265+
fl->flowi_iif == iifindex;
266+
}
267+
268+
static bool fib_rule_oif_match(const struct fib_rule *rule, int oifindex,
269+
const struct flowi *fl)
270+
{
271+
u8 oif_is_l3_master = READ_ONCE(rule->oif_is_l3_master);
272+
273+
return oif_is_l3_master ? l3mdev_fib_rule_oif_match(fl, oifindex) :
274+
fl->flowi_oif == oifindex;
275+
}
276+
259277
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
260278
struct flowi *fl, int flags,
261279
struct fib_lookup_arg *arg)
262280
{
263281
int iifindex, oifindex, ret = 0;
264282

265283
iifindex = READ_ONCE(rule->iifindex);
266-
if (iifindex && (iifindex != fl->flowi_iif))
284+
if (iifindex && !fib_rule_iif_match(rule, iifindex, fl))
267285
goto out;
268286

269287
oifindex = READ_ONCE(rule->oifindex);
270-
if (oifindex && (oifindex != fl->flowi_oif))
288+
if (oifindex && !fib_rule_oif_match(rule, oifindex, fl))
271289
goto out;
272290

273291
if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask)
@@ -567,8 +585,10 @@ static int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh,
567585
nlrule->iifindex = -1;
568586
nla_strscpy(nlrule->iifname, tb[FRA_IIFNAME], IFNAMSIZ);
569587
dev = __dev_get_by_name(net, nlrule->iifname);
570-
if (dev)
588+
if (dev) {
571589
nlrule->iifindex = dev->ifindex;
590+
nlrule->iif_is_l3_master = netif_is_l3_master(dev);
591+
}
572592
}
573593

574594
if (tb[FRA_OIFNAME]) {
@@ -577,8 +597,10 @@ static int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh,
577597
nlrule->oifindex = -1;
578598
nla_strscpy(nlrule->oifname, tb[FRA_OIFNAME], IFNAMSIZ);
579599
dev = __dev_get_by_name(net, nlrule->oifname);
580-
if (dev)
600+
if (dev) {
581601
nlrule->oifindex = dev->ifindex;
602+
nlrule->oif_is_l3_master = netif_is_l3_master(dev);
603+
}
582604
}
583605

584606
if (tb[FRA_FWMARK]) {
@@ -1218,11 +1240,17 @@ static void attach_rules(struct list_head *rules, struct net_device *dev)
12181240

12191241
list_for_each_entry(rule, rules, list) {
12201242
if (rule->iifindex == -1 &&
1221-
strcmp(dev->name, rule->iifname) == 0)
1243+
strcmp(dev->name, rule->iifname) == 0) {
12221244
WRITE_ONCE(rule->iifindex, dev->ifindex);
1245+
WRITE_ONCE(rule->iif_is_l3_master,
1246+
netif_is_l3_master(dev));
1247+
}
12231248
if (rule->oifindex == -1 &&
1224-
strcmp(dev->name, rule->oifname) == 0)
1249+
strcmp(dev->name, rule->oifname) == 0) {
12251250
WRITE_ONCE(rule->oifindex, dev->ifindex);
1251+
WRITE_ONCE(rule->oif_is_l3_master,
1252+
netif_is_l3_master(dev));
1253+
}
12261254
}
12271255
}
12281256

@@ -1231,10 +1259,14 @@ static void detach_rules(struct list_head *rules, struct net_device *dev)
12311259
struct fib_rule *rule;
12321260

12331261
list_for_each_entry(rule, rules, list) {
1234-
if (rule->iifindex == dev->ifindex)
1262+
if (rule->iifindex == dev->ifindex) {
12351263
WRITE_ONCE(rule->iifindex, -1);
1236-
if (rule->oifindex == dev->ifindex)
1264+
WRITE_ONCE(rule->iif_is_l3_master, false);
1265+
}
1266+
if (rule->oifindex == dev->ifindex) {
12371267
WRITE_ONCE(rule->oifindex, -1);
1268+
WRITE_ONCE(rule->oif_is_l3_master, false);
1269+
}
12381270
}
12391271
}
12401272

net/l3mdev/l3mdev.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,10 @@ void l3mdev_update_flow(struct net *net, struct flowi *fl)
277277
if (fl->flowi_oif) {
278278
dev = dev_get_by_index_rcu(net, fl->flowi_oif);
279279
if (dev) {
280-
if (!fl->flowi_l3mdev)
280+
if (!fl->flowi_l3mdev) {
281281
fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev);
282+
fl->flowi_flags |= FLOWI_FLAG_L3MDEV_OIF;
283+
}
282284

283285
/* oif set to L3mdev directs lookup to its table;
284286
* reset to avoid oif match in fib_lookup

0 commit comments

Comments
 (0)