Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 651b4513 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mlxsw-Support-bridge-router-interfaces-with-non-default-VLAN'



Ido Schimmel says:

====================
mlxsw: Support bridge router interfaces with non-default VLAN

Petr says:

When traffic is inserted on a router interface associated with an 802.1q
bridge, the VLAN that the traffic appears on is determined by PVID of
the bridge device itself. However currently mlxsw always configures such
traffic to be forwarded to VLAN 1, regardless of the bridge PVID.

Fix the problem by modifying the FID-handling code to assign such
traffic not to FID that corresponds to VLAN 1, but to a FID that
corresponds to the configured PVID. Bail out if there is no PVID. This
is implemented in patches #1 and #2.

From that point on, also forbid any changes to bridge device PVID,
because such changes would not be reflected. This is implemented in
patches #3, #4 and #5.

Finally in patch #6, introduce tests that use bridge as a routed
interface, and test mlxsw in both the currently-supported scenario of
using PVID 1, and the newly-supported one of using a custom PVID.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents fb223502 5b1e7f9e
Loading
Loading
Loading
Loading
+30 −12
Original line number Diff line number Diff line
@@ -163,7 +163,8 @@ struct mlxsw_sp_rif_ops {
		      const struct mlxsw_sp_rif_params *params);
	int (*configure)(struct mlxsw_sp_rif *rif);
	void (*deconfigure)(struct mlxsw_sp_rif *rif);
	struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif);
	struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif,
					 struct netlink_ext_ack *extack);
};

static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);
@@ -342,10 +343,6 @@ static void mlxsw_sp_rif_counters_free(struct mlxsw_sp_rif *rif)
	mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
}

static struct mlxsw_sp_rif *
mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
			 const struct net_device *dev);

#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1)

struct mlxsw_sp_prefix_usage {
@@ -5967,7 +5964,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
	return NOTIFY_DONE;
}

static struct mlxsw_sp_rif *
struct mlxsw_sp_rif *
mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
			 const struct net_device *dev)
{
@@ -6125,6 +6122,11 @@ const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
	return rif->dev;
}

struct mlxsw_sp_fid *mlxsw_sp_rif_fid(const struct mlxsw_sp_rif *rif)
{
	return rif->fid;
}

static struct mlxsw_sp_rif *
mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
		    const struct mlxsw_sp_rif_params *params,
@@ -6162,7 +6164,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
	rif->ops = ops;

	if (ops->fid_get) {
		fid = ops->fid_get(rif);
		fid = ops->fid_get(rif, extack);
		if (IS_ERR(fid)) {
			err = PTR_ERR(fid);
			goto err_fid_get;
@@ -6267,7 +6269,7 @@ mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan,
	}

	/* FID was already created, just take a reference */
	fid = rif->ops->fid_get(rif);
	fid = rif->ops->fid_get(rif, extack);
	err = mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port, vid);
	if (err)
		goto err_fid_port_vid_map;
@@ -6775,7 +6777,8 @@ static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
}

static struct mlxsw_sp_fid *
mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif)
mlxsw_sp_rif_subport_fid_get(struct mlxsw_sp_rif *rif,
			     struct netlink_ext_ack *extack)
{
	return mlxsw_sp_fid_rfid_get(rif->mlxsw_sp, rif->rif_index);
}
@@ -6865,9 +6868,23 @@ static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
}

static struct mlxsw_sp_fid *
mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif)
mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
			  struct netlink_ext_ack *extack)
{
	u16 vid = is_vlan_dev(rif->dev) ? vlan_dev_vlan_id(rif->dev) : 1;
	u16 vid;
	int err;

	if (is_vlan_dev(rif->dev)) {
		vid = vlan_dev_vlan_id(rif->dev);
	} else {
		err = br_vlan_get_pvid(rif->dev, &vid);
		if (!vid)
			err = -EINVAL;
		if (err) {
			NL_SET_ERR_MSG_MOD(extack, "Couldn't determine bridge PVID");
			return ERR_PTR(err);
		}
	}

	return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
}
@@ -6937,7 +6954,8 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
}

static struct mlxsw_sp_fid *
mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif)
mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
			 struct netlink_ext_ack *extack)
{
	return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
}
+3 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ struct mlxsw_sp_neigh_entry;
struct mlxsw_sp_nexthop;
struct mlxsw_sp_ipip_entry;

struct mlxsw_sp_rif *mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
					      const struct net_device *dev);
struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp,
					   u16 rif_index);
u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif);
@@ -75,6 +77,7 @@ u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev);
int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif);
u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp);
const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif);
struct mlxsw_sp_fid *mlxsw_sp_rif_fid(const struct mlxsw_sp_rif *rif);
int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
				   struct mlxsw_sp_rif *rif,
				   enum mlxsw_sp_rif_counter_dir dir,
+45 −2
Original line number Diff line number Diff line
@@ -1135,6 +1135,39 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
	return err;
}

static int
mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
				const struct net_device *br_dev,
				const struct switchdev_obj_port_vlan *vlan)
{
	struct mlxsw_sp_rif *rif;
	struct mlxsw_sp_fid *fid;
	u16 pvid;
	u16 vid;

	rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, br_dev);
	if (!rif)
		return 0;
	fid = mlxsw_sp_rif_fid(rif);
	pvid = mlxsw_sp_fid_8021q_vid(fid);

	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
		if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
			if (vid != pvid) {
				netdev_err(br_dev, "Can't change PVID, it's used by router interface\n");
				return -EBUSY;
			}
		} else {
			if (vid == pvid) {
				netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n");
				return -EBUSY;
			}
		}
	}

	return 0;
}

static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
				   const struct switchdev_obj_port_vlan *vlan,
				   struct switchdev_trans *trans)
@@ -1146,8 +1179,18 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
	struct mlxsw_sp_bridge_port *bridge_port;
	u16 vid;

	if (netif_is_bridge_master(orig_dev))
		return -EOPNOTSUPP;
	if (netif_is_bridge_master(orig_dev)) {
		int err = 0;

		if ((vlan->flags & BRIDGE_VLAN_INFO_BRENTRY) &&
		    br_vlan_enabled(orig_dev) &&
		    switchdev_trans_ph_prepare(trans))
			err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp,
							      orig_dev, vlan);
		if (!err)
			err = -EOPNOTSUPP;
		return err;
	}

	if (switchdev_trans_ph_prepare(trans))
		return 0;
+113 −0
Original line number Diff line number Diff line
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0

ALL_TESTS="
	ping_ipv4
	ping_ipv6
"
NUM_NETIFS=4
source lib.sh

h1_create()
{
	simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64
	ip -4 route add 192.0.2.128/28 vrf v$h1 nexthop via 192.0.2.2
	ip -6 route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2
}

h1_destroy()
{
	ip -6 route del 2001:db8:2::/64 vrf v$h1
	ip -4 route del 192.0.2.128/28 vrf v$h1
	simple_if_fini $h1 192.0.2.1/28 2001:db8:1::1/64
}

h2_create()
{
	simple_if_init $h2 192.0.2.130/28 2001:db8:2::2/64
	ip -4 route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.129
	ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::1
}

h2_destroy()
{
	ip -6 route del 2001:db8:1::/64 vrf v$h2
	ip -4 route del 192.0.2.0/28 vrf v$h2
	simple_if_fini $h2 192.0.2.130/28 2001:db8:2::2/64
}

router_create()
{
	ip link add name br1 type bridge vlan_filtering 1
	ip link set dev br1 up

	ip link set dev $swp1 master br1
	ip link set dev $swp1 up
	__addr_add_del br1 add 192.0.2.2/28 2001:db8:1::2/64

	ip link set dev $swp2 up
	__addr_add_del $swp2 add 192.0.2.129/28 2001:db8:2::1/64
}

router_destroy()
{
	__addr_add_del $swp2 del 192.0.2.129/28 2001:db8:2::1/64
	ip link set dev $swp2 down

	__addr_add_del br1 del 192.0.2.2/28 2001:db8:1::2/64
	ip link set dev $swp1 down
	ip link set dev $swp1 nomaster

	ip link del dev br1
}

setup_prepare()
{
	h1=${NETIFS[p1]}
	swp1=${NETIFS[p2]}

	swp2=${NETIFS[p3]}
	h2=${NETIFS[p4]}

	vrf_prepare

	h1_create
	h2_create

	router_create

	forwarding_enable
}

cleanup()
{
	pre_cleanup

	forwarding_restore

	router_destroy

	h2_destroy
	h1_destroy

	vrf_cleanup
}

ping_ipv4()
{
	ping_test $h1 192.0.2.130
}

ping_ipv6()
{
	ping6_test $h1 2001:db8:2::2
}

trap cleanup EXIT

setup_prepare
setup_wait

tests_run

exit $EXIT_STATUS
+132 −0
Original line number Diff line number Diff line
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0

ALL_TESTS="
	ping_ipv4
	ping_ipv6
	vlan
"
NUM_NETIFS=4
source lib.sh

h1_create()
{
	simple_if_init $h1
	vlan_create $h1 555 v$h1 192.0.2.1/28 2001:db8:1::1/64
	ip -4 route add 192.0.2.128/28 vrf v$h1 nexthop via 192.0.2.2
	ip -6 route add 2001:db8:2::/64 vrf v$h1 nexthop via 2001:db8:1::2
}

h1_destroy()
{
	ip -6 route del 2001:db8:2::/64 vrf v$h1
	ip -4 route del 192.0.2.128/28 vrf v$h1
	vlan_destroy $h1 555
	simple_if_fini $h1
}

h2_create()
{
	simple_if_init $h2 192.0.2.130/28 2001:db8:2::2/64
	ip -4 route add 192.0.2.0/28 vrf v$h2 nexthop via 192.0.2.129
	ip -6 route add 2001:db8:1::/64 vrf v$h2 nexthop via 2001:db8:2::1
}

h2_destroy()
{
	ip -6 route del 2001:db8:1::/64 vrf v$h2
	ip -4 route del 192.0.2.0/28 vrf v$h2
	simple_if_fini $h2 192.0.2.130/28
}

router_create()
{
	ip link add name br1 type bridge vlan_filtering 1
	ip link set dev br1 up

	ip link set dev $swp1 master br1
	ip link set dev $swp1 up

	bridge vlan add dev br1 vid 555 self pvid untagged
	bridge vlan add dev $swp1 vid 555

	__addr_add_del br1 add 192.0.2.2/28 2001:db8:1::2/64

	ip link set dev $swp2 up
	__addr_add_del $swp2 add 192.0.2.129/28 2001:db8:2::1/64
}

router_destroy()
{
	__addr_add_del $swp2 del 192.0.2.129/28 2001:db8:2::1/64
	ip link set dev $swp2 down

	__addr_add_del br1 del 192.0.2.2/28 2001:db8:1::2/64
	ip link set dev $swp1 down
	ip link set dev $swp1 nomaster

	ip link del dev br1
}

setup_prepare()
{
	h1=${NETIFS[p1]}
	swp1=${NETIFS[p2]}

	swp2=${NETIFS[p3]}
	h2=${NETIFS[p4]}

	vrf_prepare

	h1_create
	h2_create

	router_create

	forwarding_enable
}

cleanup()
{
	pre_cleanup

	forwarding_restore

	router_destroy

	h2_destroy
	h1_destroy

	vrf_cleanup
}

vlan()
{
	RET=0

	bridge vlan add dev br1 vid 333 self
	check_err $? "Can't add a non-PVID VLAN"
	bridge vlan del dev br1 vid 333 self
	check_err $? "Can't remove a non-PVID VLAN"

	log_test "vlan"
}

ping_ipv4()
{
	ping_test $h1 192.0.2.130
}

ping_ipv6()
{
	ping6_test $h1 2001:db8:2::2
}

trap cleanup EXIT

setup_prepare
setup_wait

tests_run

exit $EXIT_STATUS