Commit 277cc13a authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

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: default avatarJakub Kicinski <kuba@kernel.org>
parents 12f2d033 f9c87590
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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;
};

+1 −0
Original line number Diff line number Diff line
@@ -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;
+27 −0
Original line number Diff line number Diff line
@@ -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)
{
+40 −8
Original line number Diff line number Diff line
@@ -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);
		}
	}
}

+3 −1
Original line number Diff line number Diff line
@@ -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
Loading