Nikolay Aleksandrov says:

====================
bonding: properly restore flags when bond changes ether type

A bug was reported by syzbot[1] that causes a warning and a myriad of
other potential issues if a bond, that is also a slave, fails to enslave a
non-eth device. While fixing that bug I found that we have the same
issues when such enslave passes and after that the bond changes back to
ARPHRD_ETHER (again due to ether_setup). This set fixes all issues by
extracting the ether_setup() sequence in a helper which does the right
thing about bond flags when it needs to change back to ARPHRD_ETHER. It
also adds selftests for these cases.

Patch 01 adds the new bond_ether_setup helper and fixes the issues when a
bond device changes its ether type due to successful enslave. Patch 02
fixes the issues when it changes its ether type due to an unsuccessful
enslave. Note we need two patches because the bugs were introduced by
different commits. Patch 03 adds the new selftests.

Due to the comment adjustment and squash, could you please review
patch 01 again? I've kept the other acks since there were no code
changes.

v3: squash the helper patch and the first fix, adjust the comment above
    it to be explicit about the bond device, no code changes
v2: new set, all patches are new due to new approach of fixing these bugs

[1] https://syzkaller.appspot.com/bug?id=391c7b1f6522182899efba27d891f1743e8eb3ef
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2023-03-17 07:56:41 +00:00
Родитель 53515a052a 222c94ec0a
Коммит f5e305e63b
3 изменённых файлов: 103 добавлений и 8 удалений

Просмотреть файл

@ -1775,6 +1775,19 @@ void bond_lower_state_changed(struct slave *slave)
slave_err(bond_dev, slave_dev, "Error: %s\n", errmsg); \
} while (0)
/* The bonding driver uses ether_setup() to convert a master bond device
* to ARPHRD_ETHER, that resets the target netdevice's flags so we always
* have to restore the IFF_MASTER flag, and only restore IFF_SLAVE if it was set
*/
static void bond_ether_setup(struct net_device *bond_dev)
{
unsigned int slave_flag = bond_dev->flags & IFF_SLAVE;
ether_setup(bond_dev);
bond_dev->flags |= IFF_MASTER | slave_flag;
bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
}
/* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
struct netlink_ext_ack *extack)
@ -1866,10 +1879,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
if (slave_dev->type != ARPHRD_ETHER)
bond_setup_by_slave(bond_dev, slave_dev);
else {
ether_setup(bond_dev);
bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
}
else
bond_ether_setup(bond_dev);
call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE,
bond_dev);
@ -2289,9 +2300,7 @@ err_undo_flags:
eth_hw_addr_random(bond_dev);
if (bond_dev->type != ARPHRD_ETHER) {
dev_close(bond_dev);
ether_setup(bond_dev);
bond_dev->flags |= IFF_MASTER;
bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
bond_ether_setup(bond_dev);
}
}

Просмотреть файл

@ -8,7 +8,8 @@ TEST_PROGS := \
dev_addr_lists.sh \
mode-1-recovery-updelay.sh \
mode-2-recovery-updelay.sh \
option_prio.sh
option_prio.sh \
bond-eth-type-change.sh
TEST_FILES := \
lag_lib.sh \

Просмотреть файл

@ -0,0 +1,85 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test bond device ether type changing
#
ALL_TESTS="
bond_test_unsuccessful_enslave_type_change
bond_test_successful_enslave_type_change
"
REQUIRE_MZ=no
NUM_NETIFS=0
lib_dir=$(dirname "$0")
source "$lib_dir"/net_forwarding_lib.sh
bond_check_flags()
{
local bonddev=$1
ip -d l sh dev "$bonddev" | grep -q "MASTER"
check_err $? "MASTER flag is missing from the bond device"
ip -d l sh dev "$bonddev" | grep -q "SLAVE"
check_err $? "SLAVE flag is missing from the bond device"
}
# test enslaved bond dev type change from ARPHRD_ETHER and back
# this allows us to test both MASTER and SLAVE flags at once
bond_test_enslave_type_change()
{
local test_success=$1
local devbond0="test-bond0"
local devbond1="test-bond1"
local devbond2="test-bond2"
local nonethdev="test-noneth0"
# create a non-ARPHRD_ETHER device for testing (e.g. nlmon type)
ip link add name "$nonethdev" type nlmon
check_err $? "could not create a non-ARPHRD_ETHER device (nlmon)"
ip link add name "$devbond0" type bond
if [ $test_success -eq 1 ]; then
# we need devbond0 in active-backup mode to successfully enslave nonethdev
ip link set dev "$devbond0" type bond mode active-backup
check_err $? "could not change bond mode to active-backup"
fi
ip link add name "$devbond1" type bond
ip link add name "$devbond2" type bond
ip link set dev "$devbond0" master "$devbond1"
check_err $? "could not enslave $devbond0 to $devbond1"
# change bond type to non-ARPHRD_ETHER
ip link set dev "$nonethdev" master "$devbond0" 1>/dev/null 2>/dev/null
ip link set dev "$nonethdev" nomaster 1>/dev/null 2>/dev/null
# restore ARPHRD_ETHER type by enslaving such device
ip link set dev "$devbond2" master "$devbond0"
check_err $? "could not enslave $devbond2 to $devbond0"
ip link set dev "$devbond1" nomaster
bond_check_flags "$devbond0"
# clean up
ip link del dev "$devbond0"
ip link del dev "$devbond1"
ip link del dev "$devbond2"
ip link del dev "$nonethdev"
}
bond_test_unsuccessful_enslave_type_change()
{
RET=0
bond_test_enslave_type_change 0
log_test "Change ether type of an enslaved bond device with unsuccessful enslave"
}
bond_test_successful_enslave_type_change()
{
RET=0
bond_test_enslave_type_change 1
log_test "Change ether type of an enslaved bond device with successful enslave"
}
tests_run
exit "$EXIT_STATUS"