Sevinj Aghayeva
2022-Aug-10 03:11 UTC
[Bridge] [PATCH RFC net-next 0/3] net: vlan: fix bridge binding behavior and add selftests
When bridge binding is enabled for a vlan interface, it is expected that the link state of the vlan interface will track the subset of the ports that are also members of the corresponding vlan, rather than that of all ports. Currently, this feature works as expected when a vlan interface is created with bridge binding enabled: ip link add link br name vlan10 type vlan id 10 protocol 802.1q \ bridge_binding on However, the feature does not work when a vlan interface is created with bridge binding disabled, and then enabled later: ip link add link br name vlan10 type vlan id 10 protocol 802.1q \ bridge_binding off ip link set vlan10 type vlan bridge_binding on After these two commands, the link state of the vlan interface continues to track that of all ports, which is inconsistent and confusing to users. This series fixes this bug and introduces two tests for the valid behavior. Sevinj Aghayeva (3): net: core: export call_netdevice_notifiers_info net: 8021q: fix bridge binding behavior for vlan interfaces selftests: net: tests for bridge binding behavior include/linux/netdevice.h | 2 + net/8021q/vlan.h | 2 +- net/8021q/vlan_dev.c | 25 ++- net/core/dev.c | 7 +- tools/testing/selftests/net/Makefile | 1 + .../selftests/net/bridge_vlan_binding_test.sh | 143 ++++++++++++++++++ 6 files changed, 172 insertions(+), 8 deletions(-) create mode 100755 tools/testing/selftests/net/bridge_vlan_binding_test.sh -- 2.25.1
Sevinj Aghayeva
2022-Aug-10 03:11 UTC
[Bridge] [PATCH RFC net-next 1/3] net: core: export call_netdevice_notifiers_info
When a bridge binding flag is changed for a vlan interface, the vlan_dev_change_flags function in the 8021q module is called. Currently, this function only sets the flag for the vlan interface and returns, which results in a buggy behavior; it also needs to let the bridge module module know that upper device for the bridge has changed by propagating NETDEV_CHANGEUPPER event. This event can be triggered using call_netdevice_notifiers_info function, so export this function. Signed-off-by: Sevinj Aghayeva <sevinj.aghayeva at gmail.com> --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2563d30736e9..d17ef56c8a06 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2898,6 +2898,8 @@ netdev_notifier_info_to_extack(const struct netdev_notifier_info *info) } int call_netdevice_notifiers(unsigned long val, struct net_device *dev); +int call_netdevice_notifiers_info(unsigned long val, + struct netdev_notifier_info *info); extern rwlock_t dev_base_lock; /* Device list lock */ diff --git a/net/core/dev.c b/net/core/dev.c index 30a1603a7225..50e7483946e0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -160,8 +160,6 @@ struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; struct list_head ptype_all __read_mostly; /* Taps */ static int netif_rx_internal(struct sk_buff *skb); -static int call_netdevice_notifiers_info(unsigned long val, - struct netdev_notifier_info *info); static int call_netdevice_notifiers_extack(unsigned long val, struct net_device *dev, struct netlink_ext_ack *extack); @@ -1927,8 +1925,8 @@ static void move_netdevice_notifiers_dev_net(struct net_device *dev, * are as for raw_notifier_call_chain(). */ -static int call_netdevice_notifiers_info(unsigned long val, - struct netdev_notifier_info *info) +int call_netdevice_notifiers_info(unsigned long val, + struct netdev_notifier_info *info) { struct net *net = dev_net(info->dev); int ret; @@ -1944,6 +1942,7 @@ static int call_netdevice_notifiers_info(unsigned long val, return ret; return raw_notifier_call_chain(&netdev_chain, val, info); } +EXPORT_SYMBOL(call_netdevice_notifiers_info); /** * call_netdevice_notifiers_info_robust - call per-netns notifier blocks -- 2.25.1
Sevinj Aghayeva
2022-Aug-10 03:11 UTC
[Bridge] [PATCH RFC net-next 2/3] net: 8021q: fix bridge binding behavior for vlan interfaces
Currently, when one creates a vlan interface with the bridge binding flag disabled (using ip link add... command) and then enables the bridge binding flag afterwards (using ip link set... command), the second command has no effect. In other words, the vlan interface does not follow the status of the ports in its vlan. The root cause of this problem is as follows. The correct bridge binding behavior depends on two flags being set: a per vlan interface flag (VLAN_FLAG_BRIDGE_BINDING) and global per bridge flag (BROPT_VLAN_BRIDGE_BINDING); the ip link set command calls vlan_dev_change_flags function, which sets only the per vlan interface flag. The correct behavior is to set/unset per bridge flag as well, depending on whether there are other vlan interfaces with bridge binding flags set. The logic for this behavior is already captured in br_vlan_upper_change function, which is called whenever NETDEV_CHANGEUPPER event occurs. This patch fixes the bridge binding behavior by triggering the NETDEV_CHANGEUPPER event from the vlan_dev_change_flags function whenever the per interface flag is changed. Signed-off-by: Sevinj Aghayeva <sevinj.aghayeva at gmail.com> --- net/8021q/vlan.h | 2 +- net/8021q/vlan_dev.c | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 5eaf38875554..71947cdcfaaa 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -130,7 +130,7 @@ void vlan_dev_set_ingress_priority(const struct net_device *dev, int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio); void vlan_dev_free_egress_priority(const struct net_device *dev); -int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask); +int vlan_dev_change_flags(struct net_device *dev, u32 flag, u32 mask); void vlan_dev_get_realdev_name(const struct net_device *dev, char *result, size_t size); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 839f2020b015..49cf4cceebef 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -208,12 +208,19 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, return 0; } +static inline bool netif_is_bridge(const struct net_device *dev) +{ + return dev->rtnl_link_ops && + !strcmp(dev->rtnl_link_ops->kind, "bridge"); +} + /* Flags are defined in the vlan_flags enum in * include/uapi/linux/if_vlan.h file. */ -int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) +int vlan_dev_change_flags(struct net_device *dev, u32 flags, u32 mask) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + struct netdev_notifier_changeupper_info info; u32 old_flags = vlan->flags; if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | @@ -223,19 +230,31 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) vlan->flags = (old_flags & ~mask) | (flags & mask); - if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) { + if (!netif_running(dev)) + return 0; + + if ((vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) { if (vlan->flags & VLAN_FLAG_GVRP) vlan_gvrp_request_join(dev); else vlan_gvrp_request_leave(dev); } - if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_MVRP) { + if ((vlan->flags ^ old_flags) & VLAN_FLAG_MVRP) { if (vlan->flags & VLAN_FLAG_MVRP) vlan_mvrp_request_join(dev); else vlan_mvrp_request_leave(dev); } + + if ((vlan->flags ^ old_flags) & VLAN_FLAG_BRIDGE_BINDING && + netif_is_bridge(vlan->real_dev)) { + info.info.dev = vlan->real_dev; + info.upper_dev = dev; + info.linking = !!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING); + call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &info.info); + } + return 0; } -- 2.25.1
Sevinj Aghayeva
2022-Aug-10 03:11 UTC
[Bridge] [PATCH RFC net-next 3/3] selftests: net: tests for bridge binding behavior
This patch adds two tests in a single file. The first of these is in function run_test_late_bridge_binding_set, which tests that when a vlan interface is created with bridge binding turned off, and later bridge binding is turned on (using ip link set... command), the vlan interface behaves accordingly, that is, it tracks the status of the ports in its vlan. The second test, which is in function run_test_multiple_vlan, tests that when there are two vlan interfaces with bridge binding turned on, turning off the bridge binding in one of the vlan interfaces does not affect the bridge binding on the other interface. Signed-off-by: Sevinj Aghayeva <sevinj.aghayeva at gmail.com> --- tools/testing/selftests/net/Makefile | 1 + .../selftests/net/bridge_vlan_binding_test.sh | 143 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100755 tools/testing/selftests/net/bridge_vlan_binding_test.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index db05b3764b77..91e86a47ce49 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -39,6 +39,7 @@ TEST_PROGS += vrf_strict_mode_test.sh TEST_PROGS += arp_ndisc_evict_nocarrier.sh TEST_PROGS += ndisc_unsolicited_na_test.sh TEST_PROGS += stress_reuseport_listen.sh +TEST_PROGS += bridge_vlan_binding_test.sh TEST_PROGS_EXTENDED := in_netns.sh setup_loopback.sh setup_veth.sh TEST_PROGS_EXTENDED += toeplitz_client.sh toeplitz.sh TEST_GEN_FILES = socket nettest diff --git a/tools/testing/selftests/net/bridge_vlan_binding_test.sh b/tools/testing/selftests/net/bridge_vlan_binding_test.sh new file mode 100755 index 000000000000..d094d847800c --- /dev/null +++ b/tools/testing/selftests/net/bridge_vlan_binding_test.sh @@ -0,0 +1,143 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +cleanup() { + # Remove interfaces created by the previous run + ip link delete veth10 2>/dev/null + ip link delete veth20 2>/dev/null + ip link delete veth30 2>/dev/null + ip link delete br_default 2>/dev/null +} + +trap cleanup EXIT + +setup() { + cleanup + + # Create a bridge and add three ports to it. + ip link add dev br_default type bridge + ip link add dev veth10 type veth peer name veth11 + ip link add dev veth20 type veth peer name veth21 + ip link add dev veth30 type veth peer name veth31 + ip link set dev veth10 master br_default + ip link set dev veth20 master br_default + ip link set dev veth30 master br_default + + # Create VLAN 10 and VLAN 20. + bridge vlan add vid 10 dev br_default self + bridge vlan add vid 20 dev br_default self + + # Add veth10 to VLAN 10 and veth20 to VLAN 20. + bridge vlan add vid 10 dev veth10 + bridge vlan add vid 20 dev veth20 + + # Bring up the ports and the bridge. + ip link set veth10 up + ip link set veth11 up + ip link set veth20 up + ip link set veth21 up + ip link set veth30 up + ip link set veth31 up + ip link set br_default up +} + +# This test checks that when a vlan interface is created with bridge +# binding off, and then bridge binding turned on using "ip link set" +# command, bridge binding is actually turned on -- this hasn't been +# the case in the past. +run_test_late_bridge_binding_set() { + setup + + # Add VLAN interface vlan10 to VLAN 10 with bridge binding off. + ip link add link br_default name vlan10 type vlan id 10 protocol \ + 802.1q bridge_binding off + + # Bring up VLAN interface. + ip link set vlan10 up + + # Turn bridge binding on for vlan10. + ip link set vlan10 type vlan bridge_binding on + + # Bring down the port in vlan 10. + ip link set veth10 down + + # Since bridge binding is turned on for vlan10 interface, it + # should be tracking only the port, veth10 in its vlan. Since + # veth10 is down, vlan10 should be down as well. + if ! ip link show vlan10 | grep -q 'state LOWERLAYERDOWN'; then + echo "FAIL - vlan10 should be LOWERLAYERDOWN but it is not" + exit 1 + fi + + # Bringe the port back up. + ip link set veth10 up + + # The vlan 10 interface should be up now. + if ! ip link show vlan10 | grep -q 'state UP'; then + echo "FAIL - vlan10 should be UP but it is not" + exit 1 + fi + + echo "OK" +} + +# This test checks that when there are multiple vlan interfaces with +# bridge binding on, turning off bride binding in one of the vlan +# interfaces does not affect the bridge binding of the other +# interface. +run_test_multiple_vlan() { + setup + + # Add VLAN interface vlan10 to VLAN 10 with bridge binding on. + ip link add link br_default name vlan10 type vlan id 10 protocol \ + 802.1q bridge_binding on + # Add VLAN interface vlan20 to VLAN 20 with bridge binding on. + ip link add link br_default name vlan20 type vlan id 20 protocol \ + 802.1q bridge_binding on + + # Bring up VLAN interfaces. + ip link set vlan10 up + ip link set vlan20 up + + # Turn bridge binding off for vlan10. + ip link set vlan10 type vlan bridge_binding off + + # Bring down the ports in vlans 10 and 20. + ip link set veth10 down + ip link set veth20 down + + # Since bridge binding is off for vlan10 interface, it should + # be tracking all of the ports in the bridge; since veth30 is + # still up, vlan10 should also be up. + if ! ip link show vlan10 | grep -q 'state UP'; then + echo "FAIL - vlan10 should be UP but it is not" + exit 1 + fi + + # Since bridge binding is on for vlan20 interface, it should + # be tracking only the ports in its vlan. This port is veth20, + # and it is down; therefore, vlan20 should be down as well. + if ! ip link show vlan20 | grep -q 'state LOWERLAYERDOWN'; then + echo "FAIL - vlan20 should be LOWERLAYERDOWN but it is not" + exit 1 + fi + + # Bring the ports back up. + ip link set veth10 up + ip link set veth20 up + + # Both vlan interfaces should be up now. + if ! ip link show vlan10 | grep -q 'state UP'; then + echo "FAIL - vlan10 should be UP but it is not" + exit 1 + fi + if ! ip link show vlan20 | grep -q 'state UP' ; then + echo "FAIL - vlan20 should be UP but it is not" + exit 1 + fi + + echo "OK" +} + +run_test_late_bridge_binding_set +run_test_multiple_vlan -- 2.25.1
Nikolay Aleksandrov
2022-Aug-10 08:54 UTC
[Bridge] [PATCH RFC net-next 0/3] net: vlan: fix bridge binding behavior and add selftests
On 10/08/2022 06:11, Sevinj Aghayeva wrote:> When bridge binding is enabled for a vlan interface, it is expected > that the link state of the vlan interface will track the subset of the > ports that are also members of the corresponding vlan, rather than > that of all ports. > > Currently, this feature works as expected when a vlan interface is > created with bridge binding enabled: > > ip link add link br name vlan10 type vlan id 10 protocol 802.1q \ > bridge_binding on > > However, the feature does not work when a vlan interface is created > with bridge binding disabled, and then enabled later: > > ip link add link br name vlan10 type vlan id 10 protocol 802.1q \ > bridge_binding off > ip link set vlan10 type vlan bridge_binding on > > After these two commands, the link state of the vlan interface > continues to track that of all ports, which is inconsistent and > confusing to users. This series fixes this bug and introduces two > tests for the valid behavior. > > Sevinj Aghayeva (3): > net: core: export call_netdevice_notifiers_info > net: 8021q: fix bridge binding behavior for vlan interfaces > selftests: net: tests for bridge binding behavior > > include/linux/netdevice.h | 2 + > net/8021q/vlan.h | 2 +- > net/8021q/vlan_dev.c | 25 ++- > net/core/dev.c | 7 +- > tools/testing/selftests/net/Makefile | 1 + > .../selftests/net/bridge_vlan_binding_test.sh | 143 ++++++++++++++++++ > 6 files changed, 172 insertions(+), 8 deletions(-) > create mode 100755 tools/testing/selftests/net/bridge_vlan_binding_test.sh >Hi, NETDEV_CHANGE event is already propagated when the vlan changes flags, NETDEV_CHANGEUPPER is used when the devices' relationship changes not their flags. The only problem you have to figure out is that the flag has changed. The fix itself must be done within the bridge, not 8021q. You can figure it out based on current bridge loose binding state and the vlan's changed state, again in the bridge's NETDEV_CHANGE handler. Unfortunately the proper fix is much more involved and will need new infra, you'll have to track the loose binding vlans in the bridge. To do that you should add logic that reflects the current vlans' loose binding state *only* for vlans that also exist in the bridge, the rest which are upper should be carrier off if they have the loose binding flag set. Alternatively you can add a new NETDEV_ notifier (using something similar to struct netdev_notifier_pre_changeaddr_info) and add link type-specific space (e.g. union of link type-specific structs) in the struct which will contain what changed for 8021q and will be properly interpreted by the bridge. The downside is that we'll generate 2 notifications when changing the loose binding flag, but on the bright side won't have to track anything in the bridge, just handle the new notifier type. This might be the easiest path, the fix is still in the bridge though, the 8021q module just needs to fill in the new struct and emit the notification on any loose binding changes, the bridge must decide if it should process it (i.e. based on upper/lower relationship). Such notifier can be also re-used by other link types to propagate link-type specific changes. Both of these avoid any direct dependencies between the bridge and 8021q. Any other suggestions that are simpler, avoid direct dependencies and solve the issue in a generic way would be appreciated. Just be careful about introducing too much unnecessary processing because we can have lots of vlan devices in a system. Cheers, Nik