summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/bonding/bond_alb.c9
-rw-r--r--drivers/net/bonding/bond_main.c44
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c15
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c86
-rw-r--r--drivers/net/hyperv/netvsc_drv.c4
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c14
-rw-r--r--drivers/net/macvlan.c4
-rw-r--r--drivers/net/tap.c2
-rw-r--r--drivers/net/team/team.c2
-rw-r--r--drivers/net/tun.c2
-rw-r--r--drivers/usb/gadget/function/u_ether.c2
-rw-r--r--include/linux/netdevice.h13
-rw-r--r--net/bridge/br.c13
-rw-r--r--net/bridge/br_if.c9
-rw-r--r--net/core/dev.c29
-rw-r--r--net/core/dev_addr_lists.c3
-rw-r--r--net/core/dev_ioctl.c2
-rw-r--r--net/core/rtnetlink.c2
-rw-r--r--net/ieee802154/nl-phy.c2
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh170
21 files changed, 385 insertions, 46 deletions
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index e82108c917a6..9431127bbc60 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1031,7 +1031,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[],
*/
memcpy(ss.__data, addr, len);
ss.ss_family = dev->type;
- if (dev_set_mac_address(dev, (struct sockaddr *)&ss)) {
+ if (dev_set_mac_address(dev, (struct sockaddr *)&ss, NULL)) {
netdev_err(slave->bond->dev, "dev_set_mac_address of dev %s failed! ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n",
dev->name);
return -EOPNOTSUPP;
@@ -1250,7 +1250,7 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
bond_hw_addr_copy(tmp_addr, slave->dev->dev_addr,
slave->dev->addr_len);
- res = dev_set_mac_address(slave->dev, addr);
+ res = dev_set_mac_address(slave->dev, addr, NULL);
/* restore net_device's hw address */
bond_hw_addr_copy(slave->dev->dev_addr, tmp_addr,
@@ -1273,7 +1273,7 @@ unwind:
bond_hw_addr_copy(tmp_addr, rollback_slave->dev->dev_addr,
rollback_slave->dev->addr_len);
dev_set_mac_address(rollback_slave->dev,
- (struct sockaddr *)&ss);
+ (struct sockaddr *)&ss, NULL);
bond_hw_addr_copy(rollback_slave->dev->dev_addr, tmp_addr,
rollback_slave->dev->addr_len);
}
@@ -1732,7 +1732,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
bond->dev->addr_len);
ss.ss_family = bond->dev->type;
/* we don't care if it can't change its mac, best effort */
- dev_set_mac_address(new_slave->dev, (struct sockaddr *)&ss);
+ dev_set_mac_address(new_slave->dev, (struct sockaddr *)&ss,
+ NULL);
bond_hw_addr_copy(new_slave->dev->dev_addr, tmp_addr,
new_slave->dev->addr_len);
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 6b34dbefa7dd..a9d597f28023 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -609,14 +609,21 @@ static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
*
* Should be called with RTNL held.
*/
-static void bond_set_dev_addr(struct net_device *bond_dev,
- struct net_device *slave_dev)
+static int bond_set_dev_addr(struct net_device *bond_dev,
+ struct net_device *slave_dev)
{
+ int err;
+
netdev_dbg(bond_dev, "bond_dev=%p slave_dev=%p slave_dev->name=%s slave_dev->addr_len=%d\n",
bond_dev, slave_dev, slave_dev->name, slave_dev->addr_len);
+ err = dev_pre_changeaddr_notify(bond_dev, slave_dev->dev_addr, NULL);
+ if (err)
+ return err;
+
memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
bond_dev->addr_assign_type = NET_ADDR_STOLEN;
call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
+ return 0;
}
static struct slave *bond_get_old_active(struct bonding *bond,
@@ -652,8 +659,12 @@ static void bond_do_fail_over_mac(struct bonding *bond,
switch (bond->params.fail_over_mac) {
case BOND_FOM_ACTIVE:
- if (new_active)
- bond_set_dev_addr(bond->dev, new_active->dev);
+ if (new_active) {
+ rv = bond_set_dev_addr(bond->dev, new_active->dev);
+ if (rv)
+ netdev_err(bond->dev, "Error %d setting MAC of slave %s\n",
+ -rv, bond->dev->name);
+ }
break;
case BOND_FOM_FOLLOW:
/* if new_active && old_active, swap them
@@ -680,7 +691,7 @@ static void bond_do_fail_over_mac(struct bonding *bond,
}
rv = dev_set_mac_address(new_active->dev,
- (struct sockaddr *)&ss);
+ (struct sockaddr *)&ss, NULL);
if (rv) {
netdev_err(bond->dev, "Error %d setting MAC of slave %s\n",
-rv, new_active->dev->name);
@@ -695,7 +706,7 @@ static void bond_do_fail_over_mac(struct bonding *bond,
ss.ss_family = old_active->dev->type;
rv = dev_set_mac_address(old_active->dev,
- (struct sockaddr *)&ss);
+ (struct sockaddr *)&ss, NULL);
if (rv)
netdev_err(bond->dev, "Error %d setting MAC of slave %s\n",
-rv, new_active->dev->name);
@@ -1489,8 +1500,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
* address to be the same as the slave's.
*/
if (!bond_has_slaves(bond) &&
- bond->dev->addr_assign_type == NET_ADDR_RANDOM)
- bond_set_dev_addr(bond->dev, slave_dev);
+ bond->dev->addr_assign_type == NET_ADDR_RANDOM) {
+ res = bond_set_dev_addr(bond->dev, slave_dev);
+ if (res)
+ goto err_undo_flags;
+ }
new_slave = bond_alloc_slave(bond);
if (!new_slave) {
@@ -1527,7 +1541,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
*/
memcpy(ss.__data, bond_dev->dev_addr, bond_dev->addr_len);
ss.ss_family = slave_dev->type;
- res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss);
+ res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss,
+ extack);
if (res) {
netdev_dbg(bond_dev, "Error %d calling set_mac_address\n", res);
goto err_restore_mtu;
@@ -1818,7 +1833,7 @@ err_restore_mac:
bond_hw_addr_copy(ss.__data, new_slave->perm_hwaddr,
new_slave->dev->addr_len);
ss.ss_family = slave_dev->type;
- dev_set_mac_address(slave_dev, (struct sockaddr *)&ss);
+ dev_set_mac_address(slave_dev, (struct sockaddr *)&ss, NULL);
}
err_restore_mtu:
@@ -1999,7 +2014,7 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_hw_addr_copy(ss.__data, slave->perm_hwaddr,
slave->dev->addr_len);
ss.ss_family = slave_dev->type;
- dev_set_mac_address(slave_dev, (struct sockaddr *)&ss);
+ dev_set_mac_address(slave_dev, (struct sockaddr *)&ss, NULL);
}
if (unregister)
@@ -3544,8 +3559,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
break;
case BOND_SETHWADDR_OLD:
case SIOCBONDSETHWADDR:
- bond_set_dev_addr(bond_dev, slave_dev);
- res = 0;
+ res = bond_set_dev_addr(bond_dev, slave_dev);
break;
case BOND_CHANGE_ACTIVE_OLD:
case SIOCBONDCHANGEACTIVE:
@@ -3732,7 +3746,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
bond_for_each_slave(bond, slave, iter) {
netdev_dbg(bond_dev, "slave %p %s\n", slave, slave->dev->name);
- res = dev_set_mac_address(slave->dev, addr);
+ res = dev_set_mac_address(slave->dev, addr, NULL);
if (res) {
/* TODO: consider downing the slave
* and retry ?
@@ -3761,7 +3775,7 @@ unwind:
break;
tmp_res = dev_set_mac_address(rollback_slave->dev,
- (struct sockaddr *)&tmp_ss);
+ (struct sockaddr *)&tmp_ss, NULL);
if (tmp_res) {
netdev_dbg(bond_dev, "unwind err %d dev %s\n",
tmp_res, rollback_slave->dev->name);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 6929dbf04a1f..5fbb4b75f472 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -65,6 +65,13 @@ static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum";
static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2";
static const char mlxsw_sp_driver_version[] = "1.0";
+static const unsigned char mlxsw_sp1_mac_mask[ETH_ALEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00
+};
+static const unsigned char mlxsw_sp2_mac_mask[ETH_ALEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00
+};
+
/* tx_hdr_version
* Tx header version.
* Must be set to 1.
@@ -4083,6 +4090,7 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->mr_tcam_ops = &mlxsw_sp1_mr_tcam_ops;
mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr;
+ mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info);
}
@@ -4098,6 +4106,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
+ mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info);
}
@@ -5325,8 +5334,10 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev))
err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev,
event, ptr);
- else if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU)
- err = mlxsw_sp_netdevice_router_port_event(dev);
+ else if (event == NETDEV_PRE_CHANGEADDR ||
+ event == NETDEV_CHANGEADDR ||
+ event == NETDEV_CHANGEMTU)
+ err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr);
else if (mlxsw_sp_is_vrf_event(event, ptr))
err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);
else if (mlxsw_sp_port_dev_check(dev))
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 41a5de4a0965..64cae57bfbc8 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -132,6 +132,7 @@ struct mlxsw_sp {
struct mlxsw_core *core;
const struct mlxsw_bus_info *bus_info;
unsigned char base_mac[ETH_ALEN];
+ const unsigned char *mac_mask;
struct mlxsw_sp_upper *lags;
int *port_to_module;
struct mlxsw_sp_sb *sb;
@@ -454,7 +455,8 @@ union mlxsw_sp_l3addr {
int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp);
-int mlxsw_sp_netdevice_router_port_event(struct net_device *dev);
+int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
+ unsigned long event, void *ptr);
void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *macvlan_dev);
int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index f49a220582da..b33ba8b66861 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -6699,6 +6699,33 @@ static int mlxsw_sp_inetaddr_macvlan_event(struct net_device *macvlan_dev,
return 0;
}
+static int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *dev,
+ const unsigned char *dev_addr,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_sp_rif *rif;
+ int i;
+
+ /* A RIF is not created for macvlan netdevs. Their MAC is used to
+ * populate the FDB
+ */
+ if (netif_is_macvlan(dev))
+ return 0;
+
+ for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
+ rif = mlxsw_sp->router->rifs[i];
+ if (rif && rif->dev != dev &&
+ !ether_addr_equal_masked(rif->dev->dev_addr, dev_addr,
+ mlxsw_sp->mac_mask)) {
+ NL_SET_ERR_MSG_MOD(extack, "All router interface MAC addresses must have the same prefix");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
unsigned long event,
struct netlink_ext_ack *extack)
@@ -6760,6 +6787,11 @@ int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
if (!mlxsw_sp_rif_should_config(rif, dev, event))
goto out;
+ err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr,
+ ivi->extack);
+ if (err)
+ goto out;
+
err = __mlxsw_sp_inetaddr_event(dev, event, ivi->extack);
out:
return notifier_from_errno(err);
@@ -6841,6 +6873,11 @@ int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused,
if (!mlxsw_sp_rif_should_config(rif, dev, event))
goto out;
+ err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr,
+ i6vi->extack);
+ if (err)
+ goto out;
+
err = __mlxsw_sp_inetaddr_event(dev, event, i6vi->extack);
out:
return notifier_from_errno(err);
@@ -6863,20 +6900,14 @@ static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
}
-int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
+static int
+mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_rif *rif)
{
- struct mlxsw_sp *mlxsw_sp;
- struct mlxsw_sp_rif *rif;
+ struct net_device *dev = rif->dev;
u16 fid_index;
int err;
- mlxsw_sp = mlxsw_sp_lower_get(dev);
- if (!mlxsw_sp)
- return 0;
-
- rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
- if (!rif)
- return 0;
fid_index = mlxsw_sp_fid_index(rif->fid);
err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, false);
@@ -6920,6 +6951,41 @@ err_rif_edit:
return err;
}
+static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
+ struct netdev_notifier_pre_changeaddr_info *info)
+{
+ struct netlink_ext_ack *extack;
+
+ extack = netdev_notifier_info_to_extack(&info->info);
+ return mlxsw_sp_router_port_check_rif_addr(rif->mlxsw_sp, rif->dev,
+ info->dev_addr, extack);
+}
+
+int mlxsw_sp_netdevice_router_port_event(struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ struct mlxsw_sp *mlxsw_sp;
+ struct mlxsw_sp_rif *rif;
+
+ mlxsw_sp = mlxsw_sp_lower_get(dev);
+ if (!mlxsw_sp)
+ return 0;
+
+ rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
+ if (!rif)
+ return 0;
+
+ switch (event) {
+ case NETDEV_CHANGEMTU: /* fall through */
+ case NETDEV_CHANGEADDR:
+ return mlxsw_sp_router_port_change_event(mlxsw_sp, rif);
+ case NETDEV_PRE_CHANGEADDR:
+ return mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr);
+ }
+
+ return 0;
+}
+
static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp,
struct net_device *l3_dev,
struct netlink_ext_ack *extack)
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 18b5584d6377..91ed15ea5883 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -1247,7 +1247,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
return -ENODEV;
if (vf_netdev) {
- err = dev_set_mac_address(vf_netdev, addr);
+ err = dev_set_mac_address(vf_netdev, addr, NULL);
if (err)
return err;
}
@@ -1258,7 +1258,7 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
} else if (vf_netdev) {
/* rollback change on VF */
memcpy(addr->sa_data, ndev->dev_addr, ETH_ALEN);
- dev_set_mac_address(vf_netdev, addr);
+ dev_set_mac_address(vf_netdev, addr, NULL);
}
return err;
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index 723a3f663c06..19bdde60680c 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -759,10 +759,13 @@ EXPORT_SYMBOL_GPL(ipvlan_link_register);
static int ipvlan_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
+ struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
+ struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct ipvl_dev *ipvlan, *next;
struct ipvl_port *port;
LIST_HEAD(lst_kill);
+ int err;
if (!netif_is_ipvlan_port(dev))
return NOTIFY_DONE;
@@ -818,6 +821,17 @@ static int ipvlan_device_event(struct notifier_block *unused,
ipvlan_adjust_mtu(ipvlan, dev);
break;
+ case NETDEV_PRE_CHANGEADDR:
+ prechaddr_info = ptr;
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ err = dev_pre_changeaddr_notify(ipvlan->dev,
+ prechaddr_info->dev_addr,
+ extack);
+ if (err)
+ return notifier_from_errno(err);
+ }
+ break;
+
case NETDEV_CHANGEADDR:
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
ether_addr_copy(ipvlan->dev->dev_addr, dev->dev_addr);
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 0da3d36b283b..fc726ce4c164 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -744,7 +744,7 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
macvlan_set_addr_change(vlan->port);
- return dev_set_mac_address(vlan->lowerdev, addr);
+ return dev_set_mac_address(vlan->lowerdev, addr, NULL);
}
if (macvlan_addr_busy(vlan->port, addr->sa_data))
@@ -1213,7 +1213,7 @@ static void macvlan_port_destroy(struct net_device *dev)
sa.sa_family = port->dev->type;
memcpy(&sa.sa_data, port->perm_addr, port->dev->addr_len);
- dev_set_mac_address(port->dev, &sa);
+ dev_set_mac_address(port->dev, &sa, NULL);
}
kfree(port);
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index f03004f37eca..443b2694130c 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -1113,7 +1113,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
rtnl_unlock();
return -ENOLINK;
}
- ret = dev_set_mac_address(tap->dev, &sa);
+ ret = dev_set_mac_address(tap->dev, &sa, NULL);
tap_put_tap_dev(tap);
rtnl_unlock();
return ret;
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 93576e0240dd..afd9d25d1992 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -59,7 +59,7 @@ static int __set_port_dev_addr(struct net_device *port_dev,
memcpy(addr.__data, dev_addr, port_dev->addr_len);
addr.ss_family = port_dev->type;
- return dev_set_mac_address(port_dev, (struct sockaddr *)&addr);
+ return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
}
static int team_port_set_orig_dev_addr(struct team_port *port)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index ea528248d7d0..72577aa35b06 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -3202,7 +3202,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
tun_debug(KERN_DEBUG, tun, "set hw address: %pM\n",
ifr.ifr_hwaddr.sa_data);
- ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
+ ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr, NULL);
break;
case TUNGETSNDBUF:
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 0f026d445e31..737bd77a575d 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -879,7 +879,7 @@ int gether_register_netdev(struct net_device *net)
sa.sa_family = net->type;
memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
rtnl_lock();
- status = dev_set_mac_address(net, &sa);
+ status = dev_set_mac_address(net, &sa, NULL);
rtnl_unlock();
if (status)
pr_warn("cannot set self ethernet address: %d\n", status);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 36ca5f50f822..811632d4d8b1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2450,7 +2450,8 @@ enum netdev_cmd {
NETDEV_REGISTER,
NETDEV_UNREGISTER,
NETDEV_CHANGEMTU, /* notify after mtu change happened */
- NETDEV_CHANGEADDR,
+ NETDEV_CHANGEADDR, /* notify after the address change */
+ NETDEV_PRE_CHANGEADDR, /* notify before the address change */
NETDEV_GOING_DOWN,
NETDEV_CHANGENAME,
NETDEV_FEAT_CHANGE,
@@ -2512,6 +2513,11 @@ struct netdev_notifier_changelowerstate_info {
void *lower_state_info; /* is lower dev state */
};
+struct netdev_notifier_pre_changeaddr_info {
+ struct netdev_notifier_info info; /* must be first */
+ const unsigned char *dev_addr;
+};
+
static inline void netdev_notifier_info_init(struct netdev_notifier_info *info,
struct net_device *dev)
{
@@ -3628,7 +3634,10 @@ int dev_set_mtu_ext(struct net_device *dev, int mtu,
int dev_set_mtu(struct net_device *, int);
int dev_change_tx_queue_len(struct net_device *, unsigned long);
void dev_set_group(struct net_device *, int);
-int dev_set_mac_address(struct net_device *, struct sockaddr *);
+int dev_pre_changeaddr_notify(struct net_device *dev, const char *addr,
+ struct netlink_ext_ack *extack);
+int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
+ struct netlink_ext_ack *extack);
int dev_change_carrier(struct net_device *, bool new_carrier);
int dev_get_phys_port_id(struct net_device *dev,
struct netdev_phys_item_id *ppid);
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 4e7cd993ce94..a5174e5001d8 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -31,6 +31,8 @@
*/
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
+ struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
+ struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net_bridge_port *p;
struct net_bridge *br;
@@ -56,6 +58,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
br_mtu_auto_adjust(br);
break;
+ case NETDEV_PRE_CHANGEADDR:
+ if (br->dev->addr_assign_type == NET_ADDR_SET)
+ break;
+ prechaddr_info = ptr;
+ err = dev_pre_changeaddr_notify(br->dev,
+ prechaddr_info->dev_addr,
+ extack);
+ if (err)
+ return notifier_from_errno(err);
+ break;
+
case NETDEV_CHANGEADDR:
spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 3b945d6369c4..41f0a696a65f 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -650,6 +650,15 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");
+ if (br->dev->addr_assign_type != NET_ADDR_SET) {
+ /* Ask for permission to use this MAC address now, even if we
+ * don't end up choosing it below.
+ */
+ err = dev_pre_changeaddr_notify(br->dev, dev->dev_addr, extack);
+ if (err)
+ goto err7;
+ }
+
err = nbp_vlan_init(p, extack);
if (err) {
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
diff --git a/net/core/dev.c b/net/core/dev.c
index 754284873355..ed9aa4a91f1f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1589,6 +1589,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd)
N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN)
N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO)
N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO)
+ N(PRE_CHANGEADDR)
}
#undef N
return "UNKNOWN_NETDEV_EVENT";
@@ -7756,13 +7757,36 @@ void dev_set_group(struct net_device *dev, int new_group)
EXPORT_SYMBOL(dev_set_group);
/**
+ * dev_pre_changeaddr_notify - Call NETDEV_PRE_CHANGEADDR.
+ * @dev: device
+ * @addr: new address
+ * @extack: netlink extended ack
+ */
+int dev_pre_changeaddr_notify(struct net_device *dev, const char *addr,
+ struct netlink_ext_ack *extack)
+{
+ struct netdev_notifier_pre_changeaddr_info info = {
+ .info.dev = dev,
+ .info.extack = extack,
+ .dev_addr = addr,
+ };
+ int rc;
+
+ rc = call_netdevice_notifiers_info(NETDEV_PRE_CHANGEADDR, &info.info);
+ return notifier_to_errno(rc);
+}
+EXPORT_SYMBOL(dev_pre_changeaddr_notify);
+
+/**
* dev_set_mac_address - Change Media Access Control Address
* @dev: device
* @sa: new address
+ * @extack: netlink extended ack
*
* Change the hardware (MAC) address of the device
*/
-int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
+int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa,
+ struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
int err;
@@ -7773,6 +7797,9 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
+ err = dev_pre_changeaddr_notify(dev, sa->sa_data, extack);
+ if (err)
+ return err;
err = ops->ndo_set_mac_address(dev, sa);
if (err)
return err;
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 81a8cd4ea3bd..a6723b306717 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -498,6 +498,9 @@ int dev_addr_add(struct net_device *dev, const unsigned char *addr,
ASSERT_RTNL();
+ err = dev_pre_changeaddr_notify(dev, addr, NULL);
+ if (err)
+ return err;
err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type);
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index da273ec3cc57..31380fd5a4e2 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -246,7 +246,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
case SIOCSIFHWADDR:
if (dev->addr_len > sizeof(struct sockaddr))
return -EINVAL;
- return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
+ return dev_set_mac_address(dev, &ifr->ifr_hwaddr, NULL);
case SIOCSIFHWBROADCAST:
if (ifr->ifr_hwaddr.sa_family != dev->type)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 3b6e551f9e69..f8bdb8adab2c 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2444,7 +2444,7 @@ static int do_setlink(const struct sk_buff *skb,
sa->sa_family = dev->type;
memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]),
dev->addr_len);
- err = dev_set_mac_address(dev, sa);
+ err = dev_set_mac_address(dev, sa, extack);
kfree(sa);
if (err)
goto errout;
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index b231e40f006a..0c25c0bcc4da 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -242,7 +242,7 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
* dev_set_mac_address require RTNL_LOCK
*/
rtnl_lock();
- rc = dev_set_mac_address(dev, &addr);
+ rc = dev_set_mac_address(dev, &addr, NULL);
rtnl_unlock();
if (rc)
goto dev_unregister;
diff --git a/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh
new file mode 100755
index 000000000000..7f78b96279f3
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh
@@ -0,0 +1,170 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test various interface configuration scenarios. Observe that configurations
+# deemed valid by mlxsw succeed, invalid configurations fail and that no traces
+# are produced. To prevent the test from passing in case traces are produced,
+# the user can set the 'kernel.panic_on_warn' and 'kernel.panic_on_oops'
+# sysctls in its environment.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+ rif_set_addr_test
+ rif_inherit_bridge_addr_test
+ rif_non_inherit_bridge_addr_test
+"
+NUM_NETIFS=2
+source $lib_dir/lib.sh
+
+setup_prepare()
+{
+ swp1=${NETIFS[p1]}
+ swp2=${NETIFS[p2]}
+
+ ip link set dev $swp1 up
+ ip link set dev $swp2 up
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ ip link set dev $swp2 down
+ ip link set dev $swp1 down
+}
+
+rif_set_addr_test()
+{
+ local swp1_mac=$(mac_get $swp1)
+ local swp2_mac=$(mac_get $swp2)
+
+ RET=0
+
+ # $swp1 and $swp2 likely got their IPv6 local addresses already, but
+ # here we need to test the transition to RIF.
+ ip addr flush dev $swp1
+ ip addr flush dev $swp2
+ sleep .1
+
+ ip addr add dev $swp1 192.0.2.1/28
+ check_err $?
+
+ ip link set dev $swp1 addr 00:11:22:33:44:55
+ check_err $?
+
+ # IP address enablement should be rejected if the MAC address prefix
+ # doesn't match other RIFs.
+ ip addr add dev $swp2 192.0.2.2/28 &>/dev/null
+ check_fail $? "IP address addition passed for a device with a wrong MAC"
+ ip addr add dev $swp2 192.0.2.2/28 2>&1 >/dev/null \
+ | grep -q mlxsw_spectrum
+ check_err $? "no extack for IP address addition"
+
+ ip link set dev $swp2 addr 00:11:22:33:44:66
+ check_err $?
+ ip addr add dev $swp2 192.0.2.2/28 &>/dev/null
+ check_err $?
+
+ # Change of MAC address of a RIF should be forbidden if the new MAC
+ # doesn't share the prefix with other MAC addresses.
+ ip link set dev $swp2 addr 00:11:22:33:00:66 &>/dev/null
+ check_fail $? "change of MAC address passed for a wrong MAC"
+ ip link set dev $swp2 addr 00:11:22:33:00:66 2>&1 >/dev/null \
+ | grep -q mlxsw_spectrum
+ check_err $? "no extack for MAC address change"
+
+ log_test "RIF - bad MAC change"
+
+ ip addr del dev $swp2 192.0.2.2/28
+ ip addr del dev $swp1 192.0.2.1/28
+
+ ip link set dev $swp2 addr $swp2_mac
+ ip link set dev $swp1 addr $swp1_mac
+}
+
+rif_inherit_bridge_addr_test()
+{
+ RET=0
+
+ # Create first RIF
+ ip addr add dev $swp1 192.0.2.1/28
+ check_err $?
+
+ # Create a FID RIF
+ ip link add name br1 up type bridge vlan_filtering 0
+ ip link set dev $swp2 master br1
+ ip addr add dev br1 192.0.2.17/28
+ check_err $?
+
+ # Prepare a device with a low MAC address
+ ip link add name d up type dummy
+ ip link set dev d addr 00:11:22:33:44:55
+
+ # Attach the device to br1. That prompts bridge address change, which
+ # should be vetoed, thus preventing the attachment.
+ ip link set dev d master br1 &>/dev/null
+ check_fail $? "Device with low MAC was permitted to attach a bridge with RIF"
+ ip link set dev d master br1 2>&1 >/dev/null \
+ | grep -q mlxsw_spectrum
+ check_err $? "no extack for bridge attach rejection"
+
+ ip link set dev $swp2 addr 00:11:22:33:44:55 &>/dev/null
+ check_fail $? "Changing swp2's MAC address permitted"
+ ip link set dev $swp2 addr 00:11:22:33:44:55 2>&1 >/dev/null \
+ | grep -q mlxsw_spectrum
+ check_err $? "no extack for bridge port MAC address change rejection"
+
+ log_test "RIF - attach port with bad MAC to bridge"
+
+ ip link del dev d
+ ip link del dev br1
+ ip addr del dev $swp1 192.0.2.1/28
+}
+
+rif_non_inherit_bridge_addr_test()
+{
+ local swp2_mac=$(mac_get $swp2)
+
+ RET=0
+
+ # Create first RIF
+ ip addr add dev $swp1 192.0.2.1/28
+ check_err $?
+
+ # Create a FID RIF
+ ip link add name br1 up type bridge vlan_filtering 0
+ ip link set dev br1 addr $swp2_mac
+ ip link set dev $swp2 master br1
+ ip addr add dev br1 192.0.2.17/28
+ check_err $?
+
+ # Prepare a device with a low MAC address
+ ip link add name d up type dummy
+ ip link set dev d addr 00:11:22:33:44:55
+
+ # Attach the device to br1. Since the bridge address was set, it should
+ # work.
+ ip link set dev d master br1 &>/dev/null
+ check_err $? "Could not attach a device with low MAC to a bridge with RIF"
+
+ # Port MAC address change should be allowed for a bridge with set MAC.
+ ip link set dev $swp2 addr 00:11:22:33:44:55
+ check_err $? "Changing swp2's MAC address not permitted"
+
+ log_test "RIF - attach port with bad MAC to bridge with set MAC"
+
+ ip link set dev $swp2 addr $swp2_mac
+ ip link del dev d
+ ip link del dev br1
+ ip addr del dev $swp1 192.0.2.1/28
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS