Skip to content

Commit

Permalink
Merge branch 'Introduce-NETDEV_PRE_CHANGEADDR'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Dec 14, 2018
2 parents 95302c3 + 9651ee1 commit 522185d
Show file tree
Hide file tree
Showing 21 changed files with 385 additions and 46 deletions.
9 changes: 5 additions & 4 deletions drivers/net/bonding/bond_alb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
44 changes: 29 additions & 15 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 ?
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 13 additions & 2 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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))
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/ethernet/mellanox/mlxsw/spectrum.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
86 changes: 76 additions & 10 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/hyperv/netvsc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions drivers/net/ipvlan/ipvlan_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 522185d

Please sign in to comment.