提交 522185d5 编写于 作者: D David S. Miller

Merge branch 'Introduce-NETDEV_PRE_CHANGEADDR'

Petr Machata says:

====================
Introduce NETDEV_PRE_CHANGEADDR

Spectrum devices have a limitation that all router interfaces need to
have the same address prefix. In Spectrum-1, the requirement is for the
initial 38 bits of all RIFs to be the same, in Spectrum-2 the limit is
36 bits. Currently violations of this requirement are not diagnosed. At
the same time, if the condition is not upheld, the mismatched MAC
address ends up overwriting the common prefix, and all RIF MAC addresses
silently change to the new prefix.

It is therefore desirable to be able at least to diagnose the issue, and
better to reject attempts to change MAC addresses in ways that is
incompatible with the device.

Currently MAC address changes are notified through emission of
NETDEV_CHANGEADDR, which is done after the change. Extending this
message to allow vetoing is certainly possible, but several other
notification types have instead adopted a simple two-stage approach:
first a "pre" notification is sent to make sure all interested parties
are OK with the change that's about to be done. Then the change is done,
and afterwards a "post" notification is sent.

This dual approach is easier to use: when the change is vetoed, nothing
has changed yet, and it's therefore unnecessary to roll anything back.
Therefore this patchset introduces it for NETDEV_CHANGEADDR as well.

One prominent path to emitting NETDEV_CHANGEADDR is through
dev_set_mac_address(). Therefore in patch #1, give this function an
extack argument, so that a textual reason for rejection (or a warning)
can be communicated back to the user.

In patch #2, add the new notification type. In patch #3, have dev.c emit
the notification for instances of dev_addr change, or addition of an
address to dev_addrs list.

In patches #4 and #5, extend the bridge driver to handle and emit the
new notifier.

In patch #6, change IPVLAN to emit the new notifier.

Likewise for bonding driver in patches #7 and #8. Note that the team
driver doesn't need this treatment, as it goes through
dev_set_mac_address().

In patches #9, #10 and #11 adapt mlxsw to veto MAC addresses on router
interfaces, if they violate the requirement that all RIF MAC addresses
have the same prefix.

Finally in patches #12 and #13, add a test for vetoing of a direct
change of a port device MAC, and indirect change of a bridge MAC.
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
......@@ -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 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
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);
......
......@@ -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 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
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 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
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);
......
......@@ -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))
......
......@@ -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,
......
......@@ -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 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev)
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)
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
......
......@@ -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)
......
......@@ -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:
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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");
......
......@@ -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";
......@@ -7755,14 +7756,37 @@ 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;
......
......@@ -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);
......
......@@ -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)
......
......@@ -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;
......
......@@ -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;
......
#!/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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册