Merge branch 'vxlan-FDB-veto'

Petr Machata says:

====================
vxlan: Allow vetoing FDB operations

mlxsw does not implement handling of the more advanced types of VXLAN
FDB entries. In order to provide visibility to users, it is important to
be able to reject such FDB entries, ideally with an explanation passed
in extended ack. This patch set implements this.

In patches #1-#4, vxlan is gradually transformed to support vetoing of
FDB entries added (or modified) through vxlan_fdb_update(), and the
default FDB entry added in __vxlan_dev_create().

Patches #5-#7 deal with vxlan_changelink(). The existing code recognizes
that vxlan_fdb_update() may fail, but doesn't attempt to keep things
intact if it does. These patches change the function in several steps to
gracefully handle vetoes (or other failures).

Then in patches #8-#11, extack arguments are added, respectively, to
ndo_fdb_add(), mlxsw's mlxsw_sp_nve_ops.fdb_replay, the functions that
connect to the VXLAN vetoing code, and call_switchdev_notifiers(). Note
that call_switchdev_blocking_notifiers() already does support extack.

Finally in patch #12, mlxsw is extended to add extack messages to
rejected FDB entries. In patch #13, the functionality is tested.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-01-17 15:18:47 -08:00
commit 039d52e15e
27 changed files with 427 additions and 194 deletions

View File

@ -196,7 +196,7 @@ The switch device will learn/forget source MAC address/VLAN on ingress packets
and notify the switch driver of the mac/vlan/port tuples. The switch driver, and notify the switch driver of the mac/vlan/port tuples. The switch driver,
in turn, will notify the bridge driver using the switchdev notifier call: in turn, will notify the bridge driver using the switchdev notifier call:
err = call_switchdev_notifiers(val, dev, info); err = call_switchdev_notifiers(val, dev, info, extack);
Where val is SWITCHDEV_FDB_ADD when learning and SWITCHDEV_FDB_DEL when Where val is SWITCHDEV_FDB_ADD when learning and SWITCHDEV_FDB_DEL when
forgetting, and info points to a struct switchdev_notifier_fdb_info. On forgetting, and info points to a struct switchdev_notifier_fdb_info. On

View File

@ -11644,7 +11644,8 @@ static int i40e_get_phys_port_id(struct net_device *netdev,
static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, const unsigned char *addr, u16 vid,
u16 flags) u16 flags,
struct netlink_ext_ack *extack)
{ {
struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_pf *pf = np->vsi->back; struct i40e_pf *pf = np->vsi->back;

View File

@ -2438,7 +2438,8 @@ static void ice_set_rx_mode(struct net_device *netdev)
*/ */
static int ice_fdb_add(struct ndmsg *ndm, struct nlattr __always_unused *tb[], static int ice_fdb_add(struct ndmsg *ndm, struct nlattr __always_unused *tb[],
struct net_device *dev, const unsigned char *addr, struct net_device *dev, const unsigned char *addr,
u16 vid, u16 flags) u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{ {
int err; int err;

View File

@ -2486,7 +2486,8 @@ static int igb_set_features(struct net_device *netdev,
static int igb_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int igb_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, const unsigned char *addr, u16 vid,
u16 flags) u16 flags,
struct netlink_ext_ack *extack)
{ {
/* guarantee we can provide a unique filter for the unicast address */ /* guarantee we can provide a unique filter for the unicast address */
if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) { if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) {

View File

@ -9910,7 +9910,8 @@ static void ixgbe_del_udp_tunnel_port(struct net_device *dev,
static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, const unsigned char *addr, u16 vid,
u16 flags) u16 flags,
struct netlink_ext_ack *extack)
{ {
/* guarantee we can provide a unique filter for the unicast address */ /* guarantee we can provide a unique filter for the unicast address */
if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) { if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) {

View File

@ -841,11 +841,9 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
nve->config = config; nve->config = config;
err = ops->fdb_replay(params->dev, params->vni); err = ops->fdb_replay(params->dev, params->vni, extack);
if (err) { if (err)
NL_SET_ERR_MSG_MOD(extack, "Failed to offload the FDB");
goto err_fdb_replay; goto err_fdb_replay;
}
return 0; return 0;

View File

@ -41,7 +41,8 @@ struct mlxsw_sp_nve_ops {
int (*init)(struct mlxsw_sp_nve *nve, int (*init)(struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_config *config); const struct mlxsw_sp_nve_config *config);
void (*fini)(struct mlxsw_sp_nve *nve); void (*fini)(struct mlxsw_sp_nve *nve);
int (*fdb_replay)(const struct net_device *nve_dev, __be32 vni); int (*fdb_replay)(const struct net_device *nve_dev, __be32 vni,
struct netlink_ext_ack *extack);
void (*fdb_clear_offload)(const struct net_device *nve_dev, __be32 vni); void (*fdb_clear_offload)(const struct net_device *nve_dev, __be32 vni);
}; };

View File

@ -212,11 +212,13 @@ static void mlxsw_sp1_nve_vxlan_fini(struct mlxsw_sp_nve *nve)
} }
static int static int
mlxsw_sp_nve_vxlan_fdb_replay(const struct net_device *nve_dev, __be32 vni) mlxsw_sp_nve_vxlan_fdb_replay(const struct net_device *nve_dev, __be32 vni,
struct netlink_ext_ack *extack)
{ {
if (WARN_ON(!netif_is_vxlan(nve_dev))) if (WARN_ON(!netif_is_vxlan(nve_dev)))
return -EINVAL; return -EINVAL;
return vxlan_fdb_replay(nve_dev, vni, &mlxsw_sp_switchdev_notifier); return vxlan_fdb_replay(nve_dev, vni, &mlxsw_sp_switchdev_notifier,
extack);
} }
static void static void

View File

@ -7294,7 +7294,8 @@ static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
info.addr = mac; info.addr = mac;
info.vid = vid; info.vid = vid;
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info); call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info,
NULL);
} }
static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = { static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
@ -7381,7 +7382,8 @@ static void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
info.addr = mac; info.addr = mac;
info.vid = 0; info.vid = 0;
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info); call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info,
NULL);
} }
static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = { static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {

View File

@ -2443,7 +2443,7 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
ether_addr_copy(info.eth_addr, mac); ether_addr_copy(info.eth_addr, mac);
info.vni = vni; info.vni = vni;
info.offloaded = adding; info.offloaded = adding;
call_switchdev_notifiers(type, dev, &info.info); call_switchdev_notifiers(type, dev, &info.info, NULL);
} }
static void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev, static void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev,
@ -2468,7 +2468,7 @@ mlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type,
info.addr = mac; info.addr = mac;
info.vid = vid; info.vid = vid;
info.offloaded = offloaded; info.offloaded = offloaded;
call_switchdev_notifiers(type, dev, &info.info); call_switchdev_notifiers(type, dev, &info.info, NULL);
} }
static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
@ -2819,7 +2819,7 @@ mlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp,
return; return;
vxlan_fdb_info.offloaded = true; vxlan_fdb_info.offloaded = true;
call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
&vxlan_fdb_info.info); &vxlan_fdb_info.info, NULL);
mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
vxlan_fdb_info.eth_addr, vxlan_fdb_info.eth_addr,
fdb_info->vid, dev, true); fdb_info->vid, dev, true);
@ -2832,7 +2832,7 @@ mlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp,
false); false);
vxlan_fdb_info.offloaded = false; vxlan_fdb_info.offloaded = false;
call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
&vxlan_fdb_info.info); &vxlan_fdb_info.info, NULL);
break; break;
} }
} }
@ -2977,7 +2977,7 @@ mlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp,
} }
vxlan_fdb_info->offloaded = true; vxlan_fdb_info->offloaded = true;
call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
&vxlan_fdb_info->info); &vxlan_fdb_info->info, NULL);
mlxsw_sp_fid_put(fid); mlxsw_sp_fid_put(fid);
return; return;
} }
@ -2998,7 +2998,7 @@ mlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp,
goto err_fdb_tunnel_uc_op; goto err_fdb_tunnel_uc_op;
vxlan_fdb_info->offloaded = true; vxlan_fdb_info->offloaded = true;
call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev,
&vxlan_fdb_info->info); &vxlan_fdb_info->info, NULL);
mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
vxlan_fdb_info->eth_addr, vid, dev, true); vxlan_fdb_info->eth_addr, vid, dev, true);
@ -3099,23 +3099,34 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *
struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev); struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev);
struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
struct vxlan_config *cfg = &vxlan->cfg; struct vxlan_config *cfg = &vxlan->cfg;
struct netlink_ext_ack *extack;
extack = switchdev_notifier_info_to_extack(info);
vxlan_fdb_info = container_of(info, vxlan_fdb_info = container_of(info,
struct switchdev_notifier_vxlan_fdb_info, struct switchdev_notifier_vxlan_fdb_info,
info); info);
if (vxlan_fdb_info->remote_port != cfg->dst_port) if (vxlan_fdb_info->remote_port != cfg->dst_port) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default remote port is not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (vxlan_fdb_info->remote_vni != cfg->vni) }
if (vxlan_fdb_info->remote_vni != cfg->vni ||
vxlan_fdb_info->vni != cfg->vni) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default VNI is not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (vxlan_fdb_info->vni != cfg->vni) }
if (vxlan_fdb_info->remote_ifindex) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Local interface is not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (vxlan_fdb_info->remote_ifindex) }
if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast MAC addresses not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) }
return -EOPNOTSUPP; if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) {
if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast destination IP is not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
switchdev_work->vxlan_fdb_info = *vxlan_fdb_info; switchdev_work->vxlan_fdb_info = *vxlan_fdb_info;

View File

@ -721,7 +721,8 @@ static void ocelot_get_stats64(struct net_device *dev,
static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr, struct net_device *dev, const unsigned char *addr,
u16 vid, u16 flags) u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port *port = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = port->ocelot;

View File

@ -396,7 +396,8 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *netdev, struct net_device *netdev,
const unsigned char *addr, u16 vid, u16 flags) const unsigned char *addr, u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{ {
struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_adapter *adapter = netdev_priv(netdev);
int err = 0; int err = 0;

View File

@ -2725,7 +2725,7 @@ rocker_fdb_offload_notify(struct rocker_port *rocker_port,
info.vid = recv_info->vid; info.vid = recv_info->vid;
info.offloaded = true; info.offloaded = true;
call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
rocker_port->dev, &info.info); rocker_port->dev, &info.info, NULL);
} }
static void rocker_switchdev_event_work(struct work_struct *work) static void rocker_switchdev_event_work(struct work_struct *work)

View File

@ -1833,10 +1833,10 @@ static void ofdpa_port_fdb_learn_work(struct work_struct *work)
rtnl_lock(); rtnl_lock();
if (learned && removing) if (learned && removing)
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
lw->ofdpa_port->dev, &info.info); lw->ofdpa_port->dev, &info.info, NULL);
else if (learned && !removing) else if (learned && !removing)
call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
lw->ofdpa_port->dev, &info.info); lw->ofdpa_port->dev, &info.info, NULL);
rtnl_unlock(); rtnl_unlock();
kfree(work); kfree(work);

View File

@ -963,7 +963,8 @@ static int macvlan_vlan_rx_kill_vid(struct net_device *dev,
static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, const unsigned char *addr, u16 vid,
u16 flags) u16 flags,
struct netlink_ext_ack *extack)
{ {
struct macvlan_dev *vlan = netdev_priv(dev); struct macvlan_dev *vlan = netdev_priv(dev);
int err = -EINVAL; int err = -EINVAL;

View File

@ -361,10 +361,11 @@ errout:
static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan, static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,
const struct vxlan_fdb *fdb, const struct vxlan_fdb *fdb,
const struct vxlan_rdst *rd, const struct vxlan_rdst *rd,
struct netlink_ext_ack *extack,
struct switchdev_notifier_vxlan_fdb_info *fdb_info) struct switchdev_notifier_vxlan_fdb_info *fdb_info)
{ {
fdb_info->info.dev = vxlan->dev; fdb_info->info.dev = vxlan->dev;
fdb_info->info.extack = NULL; fdb_info->info.extack = extack;
fdb_info->remote_ip = rd->remote_ip; fdb_info->remote_ip = rd->remote_ip;
fdb_info->remote_port = rd->remote_port; fdb_info->remote_port = rd->remote_port;
fdb_info->remote_vni = rd->remote_vni; fdb_info->remote_vni = rd->remote_vni;
@ -375,41 +376,50 @@ static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,
fdb_info->added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER; fdb_info->added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER;
} }
static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan, static int vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
struct vxlan_fdb *fdb, struct vxlan_fdb *fdb,
struct vxlan_rdst *rd, struct vxlan_rdst *rd,
bool adding) bool adding,
struct netlink_ext_ack *extack)
{ {
struct switchdev_notifier_vxlan_fdb_info info; struct switchdev_notifier_vxlan_fdb_info info;
enum switchdev_notifier_type notifier_type; enum switchdev_notifier_type notifier_type;
int ret;
if (WARN_ON(!rd)) if (WARN_ON(!rd))
return; return 0;
notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE
: SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE; : SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE;
vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, &info); vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, NULL, &info);
call_switchdev_notifiers(notifier_type, vxlan->dev, ret = call_switchdev_notifiers(notifier_type, vxlan->dev,
&info.info); &info.info, extack);
return notifier_to_errno(ret);
} }
static void vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, static int vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,
struct vxlan_rdst *rd, int type, bool swdev_notify) struct vxlan_rdst *rd, int type, bool swdev_notify,
struct netlink_ext_ack *extack)
{ {
int err;
if (swdev_notify) { if (swdev_notify) {
switch (type) { switch (type) {
case RTM_NEWNEIGH: case RTM_NEWNEIGH:
vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd, err = vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
true); true, extack);
if (err)
return err;
break; break;
case RTM_DELNEIGH: case RTM_DELNEIGH:
vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd, vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
false); false, extack);
break; break;
} }
} }
__vxlan_fdb_notify(vxlan, fdb, rd, type); __vxlan_fdb_notify(vxlan, fdb, rd, type);
return 0;
} }
static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa) static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa)
@ -423,7 +433,7 @@ static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa)
.remote_vni = cpu_to_be32(VXLAN_N_VID), .remote_vni = cpu_to_be32(VXLAN_N_VID),
}; };
vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true); vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true, NULL);
} }
static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN]) static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
@ -435,7 +445,7 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
memcpy(f.eth_addr, eth_addr, ETH_ALEN); memcpy(f.eth_addr, eth_addr, ETH_ALEN);
vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true); vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true, NULL);
} }
/* Hash Ethernet address */ /* Hash Ethernet address */
@ -545,7 +555,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
} }
rdst = first_remote_rcu(f); rdst = first_remote_rcu(f);
vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, fdb_info); vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, NULL, fdb_info);
out: out:
rcu_read_unlock(); rcu_read_unlock();
@ -556,19 +566,21 @@ EXPORT_SYMBOL_GPL(vxlan_fdb_find_uc);
static int vxlan_fdb_notify_one(struct notifier_block *nb, static int vxlan_fdb_notify_one(struct notifier_block *nb,
const struct vxlan_dev *vxlan, const struct vxlan_dev *vxlan,
const struct vxlan_fdb *f, const struct vxlan_fdb *f,
const struct vxlan_rdst *rdst) const struct vxlan_rdst *rdst,
struct netlink_ext_ack *extack)
{ {
struct switchdev_notifier_vxlan_fdb_info fdb_info; struct switchdev_notifier_vxlan_fdb_info fdb_info;
int rc; int rc;
vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, &fdb_info); vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, extack, &fdb_info);
rc = nb->notifier_call(nb, SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE, rc = nb->notifier_call(nb, SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
&fdb_info); &fdb_info);
return notifier_to_errno(rc); return notifier_to_errno(rc);
} }
int vxlan_fdb_replay(const struct net_device *dev, __be32 vni, int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
struct notifier_block *nb) struct notifier_block *nb,
struct netlink_ext_ack *extack)
{ {
struct vxlan_dev *vxlan; struct vxlan_dev *vxlan;
struct vxlan_rdst *rdst; struct vxlan_rdst *rdst;
@ -586,7 +598,8 @@ int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
if (f->vni == vni) { if (f->vni == vni) {
list_for_each_entry(rdst, &f->remotes, list) { list_for_each_entry(rdst, &f->remotes, list) {
rc = vxlan_fdb_notify_one(nb, vxlan, rc = vxlan_fdb_notify_one(nb, vxlan,
f, rdst); f, rdst,
extack);
if (rc) if (rc)
goto out; goto out;
} }
@ -625,7 +638,7 @@ EXPORT_SYMBOL_GPL(vxlan_fdb_clear_offload);
/* Replace destination of unicast mac */ /* Replace destination of unicast mac */
static int vxlan_fdb_replace(struct vxlan_fdb *f, static int vxlan_fdb_replace(struct vxlan_fdb *f,
union vxlan_addr *ip, __be16 port, __be32 vni, union vxlan_addr *ip, __be16 port, __be32 vni,
__u32 ifindex) __u32 ifindex, struct vxlan_rdst *oldrd)
{ {
struct vxlan_rdst *rd; struct vxlan_rdst *rd;
@ -637,6 +650,7 @@ static int vxlan_fdb_replace(struct vxlan_fdb *f,
if (!rd) if (!rd)
return 0; return 0;
*oldrd = *rd;
dst_cache_reset(&rd->dst_cache); dst_cache_reset(&rd->dst_cache);
rd->remote_ip = *ip; rd->remote_ip = *ip;
rd->remote_port = port; rd->remote_port = port;
@ -826,92 +840,6 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
return 0; return 0;
} }
/* Add new entry to forwarding table -- assumes lock held */
static int vxlan_fdb_update(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags,
bool swdev_notify)
{
__u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_rdst *rd = NULL;
struct vxlan_fdb *f;
int notify = 0;
int rc;
f = __vxlan_find_mac(vxlan, mac, src_vni);
if (f) {
if (flags & NLM_F_EXCL) {
netdev_dbg(vxlan->dev,
"lost race to create %pM\n", mac);
return -EEXIST;
}
/* Do not allow an externally learned entry to take over an
* entry added by the user.
*/
if (!(fdb_flags & NTF_EXT_LEARNED) ||
!(f->flags & NTF_VXLAN_ADDED_BY_USER)) {
if (f->state != state) {
f->state = state;
f->updated = jiffies;
notify = 1;
}
if (f->flags != fdb_flags) {
f->flags = fdb_flags;
f->updated = jiffies;
notify = 1;
}
}
if ((flags & NLM_F_REPLACE)) {
/* Only change unicasts */
if (!(is_multicast_ether_addr(f->eth_addr) ||
is_zero_ether_addr(f->eth_addr))) {
notify |= vxlan_fdb_replace(f, ip, port, vni,
ifindex);
} else
return -EOPNOTSUPP;
}
if ((flags & NLM_F_APPEND) &&
(is_multicast_ether_addr(f->eth_addr) ||
is_zero_ether_addr(f->eth_addr))) {
rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd);
if (rc < 0)
return rc;
notify |= rc;
}
if (ndm_flags & NTF_USE)
f->used = jiffies;
} else {
if (!(flags & NLM_F_CREATE))
return -ENOENT;
/* Disallow replace to add a multicast entry */
if ((flags & NLM_F_REPLACE) &&
(is_multicast_ether_addr(mac) || is_zero_ether_addr(mac)))
return -EOPNOTSUPP;
netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
rc = vxlan_fdb_create(vxlan, mac, ip, state, port, src_vni,
vni, ifindex, fdb_flags, &f);
if (rc < 0)
return rc;
notify = 1;
}
if (notify) {
if (rd == NULL)
rd = first_remote_rtnl(f);
vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH, swdev_notify);
}
return 0;
}
static void vxlan_fdb_free(struct rcu_head *head) static void vxlan_fdb_free(struct rcu_head *head)
{ {
struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu); struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu);
@ -929,19 +857,162 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
{ {
struct vxlan_rdst *rd; struct vxlan_rdst *rd;
netdev_dbg(vxlan->dev, netdev_dbg(vxlan->dev, "delete %pM\n", f->eth_addr);
"delete %pM\n", f->eth_addr);
--vxlan->addrcnt; --vxlan->addrcnt;
if (do_notify) if (do_notify)
list_for_each_entry(rd, &f->remotes, list) list_for_each_entry(rd, &f->remotes, list)
vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH, vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH,
swdev_notify); swdev_notify, NULL);
hlist_del_rcu(&f->hlist); hlist_del_rcu(&f->hlist);
call_rcu(&f->rcu, vxlan_fdb_free); call_rcu(&f->rcu, vxlan_fdb_free);
} }
static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __be32 vni,
__u32 ifindex, __u16 ndm_flags,
struct vxlan_fdb *f,
bool swdev_notify,
struct netlink_ext_ack *extack)
{
__u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_rdst *rd = NULL;
struct vxlan_rdst oldrd;
int notify = 0;
int rc = 0;
int err;
/* Do not allow an externally learned entry to take over an entry added
* by the user.
*/
if (!(fdb_flags & NTF_EXT_LEARNED) ||
!(f->flags & NTF_VXLAN_ADDED_BY_USER)) {
if (f->state != state) {
f->state = state;
f->updated = jiffies;
notify = 1;
}
if (f->flags != fdb_flags) {
f->flags = fdb_flags;
f->updated = jiffies;
notify = 1;
}
}
if ((flags & NLM_F_REPLACE)) {
/* Only change unicasts */
if (!(is_multicast_ether_addr(f->eth_addr) ||
is_zero_ether_addr(f->eth_addr))) {
rc = vxlan_fdb_replace(f, ip, port, vni,
ifindex, &oldrd);
notify |= rc;
} else {
return -EOPNOTSUPP;
}
}
if ((flags & NLM_F_APPEND) &&
(is_multicast_ether_addr(f->eth_addr) ||
is_zero_ether_addr(f->eth_addr))) {
rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd);
if (rc < 0)
return rc;
notify |= rc;
}
if (ndm_flags & NTF_USE)
f->used = jiffies;
if (notify) {
if (rd == NULL)
rd = first_remote_rtnl(f);
err = vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH,
swdev_notify, extack);
if (err)
goto err_notify;
}
return 0;
err_notify:
if ((flags & NLM_F_REPLACE) && rc)
*rd = oldrd;
else if ((flags & NLM_F_APPEND) && rc)
list_del_rcu(&rd->list);
return err;
}
static int vxlan_fdb_update_create(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags,
bool swdev_notify,
struct netlink_ext_ack *extack)
{
__u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_fdb *f;
int rc;
/* Disallow replace to add a multicast entry */
if ((flags & NLM_F_REPLACE) &&
(is_multicast_ether_addr(mac) || is_zero_ether_addr(mac)))
return -EOPNOTSUPP;
netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
rc = vxlan_fdb_create(vxlan, mac, ip, state, port, src_vni,
vni, ifindex, fdb_flags, &f);
if (rc < 0)
return rc;
rc = vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH,
swdev_notify, extack);
if (rc)
goto err_notify;
return 0;
err_notify:
vxlan_fdb_destroy(vxlan, f, false, false);
return rc;
}
/* Add new entry to forwarding table -- assumes lock held */
static int vxlan_fdb_update(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags,
bool swdev_notify,
struct netlink_ext_ack *extack)
{
struct vxlan_fdb *f;
f = __vxlan_find_mac(vxlan, mac, src_vni);
if (f) {
if (flags & NLM_F_EXCL) {
netdev_dbg(vxlan->dev,
"lost race to create %pM\n", mac);
return -EEXIST;
}
return vxlan_fdb_update_existing(vxlan, ip, state, flags, port,
vni, ifindex, ndm_flags, f,
swdev_notify, extack);
} else {
if (!(flags & NLM_F_CREATE))
return -ENOENT;
return vxlan_fdb_update_create(vxlan, mac, ip, state, flags,
port, src_vni, vni, ifindex,
ndm_flags, swdev_notify, extack);
}
}
static void vxlan_dst_free(struct rcu_head *head) static void vxlan_dst_free(struct rcu_head *head)
{ {
struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu); struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu);
@ -954,7 +1025,7 @@ static void vxlan_fdb_dst_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
struct vxlan_rdst *rd, bool swdev_notify) struct vxlan_rdst *rd, bool swdev_notify)
{ {
list_del_rcu(&rd->list); list_del_rcu(&rd->list);
vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH, swdev_notify); vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH, swdev_notify, NULL);
call_rcu(&rd->rcu, vxlan_dst_free); call_rcu(&rd->rcu, vxlan_dst_free);
} }
@ -1025,7 +1096,8 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
/* Add static entry (via netlink) */ /* Add static entry (via netlink) */
static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, u16 flags) const unsigned char *addr, u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
/* struct net *net = dev_net(vxlan->dev); */ /* struct net *net = dev_net(vxlan->dev); */
@ -1055,7 +1127,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags, err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags,
port, src_vni, vni, ifindex, port, src_vni, vni, ifindex,
ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER, ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER,
true); true, extack);
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
return err; return err;
@ -1223,7 +1295,7 @@ static bool vxlan_snoop(struct net_device *dev,
rdst->remote_ip = *src_ip; rdst->remote_ip = *src_ip;
f->updated = jiffies; f->updated = jiffies;
vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH, true); vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH, true, NULL);
} else { } else {
/* learned new entry */ /* learned new entry */
spin_lock(&vxlan->hash_lock); spin_lock(&vxlan->hash_lock);
@ -1236,7 +1308,7 @@ static bool vxlan_snoop(struct net_device *dev,
vxlan->cfg.dst_port, vxlan->cfg.dst_port,
vni, vni,
vxlan->default_dst.remote_vni, vxlan->default_dst.remote_vni,
ifindex, NTF_SELF, true); ifindex, NTF_SELF, true, NULL);
spin_unlock(&vxlan->hash_lock); spin_unlock(&vxlan->hash_lock);
} }
@ -3478,9 +3550,12 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
goto errout; goto errout;
/* notify default fdb entry */ /* notify default fdb entry */
if (f) if (f) {
vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH, err = vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f),
true); RTM_NEWNEIGH, true, extack);
if (err)
goto errout;
}
list_add(&vxlan->next, &vn->vxlan_list); list_add(&vxlan->next, &vn->vxlan_list);
return 0; return 0;
@ -3727,8 +3802,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *dst = &vxlan->default_dst; struct vxlan_rdst *dst = &vxlan->default_dst;
unsigned long old_age_interval; struct net_device *lowerdev;
struct vxlan_rdst old_dst;
struct vxlan_config conf; struct vxlan_config conf;
int err; int err;
@ -3737,46 +3811,43 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (err) if (err)
return err; return err;
old_age_interval = vxlan->cfg.age_interval; err = vxlan_config_validate(vxlan->net, &conf, &lowerdev,
memcpy(&old_dst, dst, sizeof(struct vxlan_rdst)); vxlan, extack);
err = vxlan_dev_configure(vxlan->net, dev, &conf, true, extack);
if (err) if (err)
return err; return err;
if (old_age_interval != vxlan->cfg.age_interval)
mod_timer(&vxlan->age_timer, jiffies);
/* handle default dst entry */ /* handle default dst entry */
if (!vxlan_addr_equal(&dst->remote_ip, &old_dst.remote_ip)) { if (!vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip)) {
spin_lock_bh(&vxlan->hash_lock); spin_lock_bh(&vxlan->hash_lock);
if (!vxlan_addr_any(&old_dst.remote_ip)) if (!vxlan_addr_any(&conf.remote_ip)) {
__vxlan_fdb_delete(vxlan, all_zeros_mac,
old_dst.remote_ip,
vxlan->cfg.dst_port,
old_dst.remote_vni,
old_dst.remote_vni,
old_dst.remote_ifindex,
true);
if (!vxlan_addr_any(&dst->remote_ip)) {
err = vxlan_fdb_update(vxlan, all_zeros_mac, err = vxlan_fdb_update(vxlan, all_zeros_mac,
&dst->remote_ip, &conf.remote_ip,
NUD_REACHABLE | NUD_PERMANENT, NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE, NLM_F_APPEND | NLM_F_CREATE,
vxlan->cfg.dst_port, vxlan->cfg.dst_port,
dst->remote_vni, conf.vni, conf.vni,
dst->remote_vni, conf.remote_ifindex,
dst->remote_ifindex, NTF_SELF, true, extack);
NTF_SELF, true);
if (err) { if (err) {
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
return err; return err;
} }
} }
if (!vxlan_addr_any(&dst->remote_ip))
__vxlan_fdb_delete(vxlan, all_zeros_mac,
dst->remote_ip,
vxlan->cfg.dst_port,
dst->remote_vni,
dst->remote_vni,
dst->remote_ifindex,
true);
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
} }
if (conf.age_interval != vxlan->cfg.age_interval)
mod_timer(&vxlan->age_timer, jiffies);
vxlan_config_apply(dev, &conf, lowerdev, vxlan->net, true);
return 0; return 0;
} }
@ -4051,8 +4122,11 @@ vxlan_fdb_external_learn_add(struct net_device *dev,
struct switchdev_notifier_vxlan_fdb_info *fdb_info) struct switchdev_notifier_vxlan_fdb_info *fdb_info)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
struct netlink_ext_ack *extack;
int err; int err;
extack = switchdev_notifier_info_to_extack(&fdb_info->info);
spin_lock_bh(&vxlan->hash_lock); spin_lock_bh(&vxlan->hash_lock);
err = vxlan_fdb_update(vxlan, fdb_info->eth_addr, &fdb_info->remote_ip, err = vxlan_fdb_update(vxlan, fdb_info->eth_addr, &fdb_info->remote_ip,
NUD_REACHABLE, NUD_REACHABLE,
@ -4062,7 +4136,7 @@ vxlan_fdb_external_learn_add(struct net_device *dev,
fdb_info->remote_vni, fdb_info->remote_vni,
fdb_info->remote_ifindex, fdb_info->remote_ifindex,
NTF_USE | NTF_SELF | NTF_EXT_LEARNED, NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
false); false, extack);
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
return err; return err;

View File

@ -1152,7 +1152,8 @@ struct dev_ifalias {
* *
* int (*ndo_fdb_add)(struct ndmsg *ndm, struct nlattr *tb[], * int (*ndo_fdb_add)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev, * struct net_device *dev,
* const unsigned char *addr, u16 vid, u16 flags) * const unsigned char *addr, u16 vid, u16 flags,
* struct netlink_ext_ack *extack);
* Adds an FDB entry to dev for addr. * Adds an FDB entry to dev for addr.
* int (*ndo_fdb_del)(struct ndmsg *ndm, struct nlattr *tb[], * int (*ndo_fdb_del)(struct ndmsg *ndm, struct nlattr *tb[],
* struct net_device *dev, * struct net_device *dev,
@ -1376,7 +1377,8 @@ struct net_device_ops {
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, const unsigned char *addr,
u16 vid, u16 vid,
u16 flags); u16 flags,
struct netlink_ext_ack *extack);
int (*ndo_fdb_del)(struct ndmsg *ndm, int (*ndo_fdb_del)(struct ndmsg *ndm,
struct nlattr *tb[], struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,

View File

@ -195,7 +195,8 @@ int switchdev_port_obj_del(struct net_device *dev,
int register_switchdev_notifier(struct notifier_block *nb); int register_switchdev_notifier(struct notifier_block *nb);
int unregister_switchdev_notifier(struct notifier_block *nb); int unregister_switchdev_notifier(struct notifier_block *nb);
int call_switchdev_notifiers(unsigned long val, struct net_device *dev, int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info); struct switchdev_notifier_info *info,
struct netlink_ext_ack *extack);
int register_switchdev_blocking_notifier(struct notifier_block *nb); int register_switchdev_blocking_notifier(struct notifier_block *nb);
int unregister_switchdev_blocking_notifier(struct notifier_block *nb); int unregister_switchdev_blocking_notifier(struct notifier_block *nb);
@ -267,7 +268,8 @@ static inline int unregister_switchdev_notifier(struct notifier_block *nb)
static inline int call_switchdev_notifiers(unsigned long val, static inline int call_switchdev_notifiers(unsigned long val,
struct net_device *dev, struct net_device *dev,
struct switchdev_notifier_info *info) struct switchdev_notifier_info *info,
struct netlink_ext_ack *extack)
{ {
return NOTIFY_DONE; return NOTIFY_DONE;
} }

View File

@ -428,7 +428,8 @@ struct switchdev_notifier_vxlan_fdb_info {
int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni, int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
struct switchdev_notifier_vxlan_fdb_info *fdb_info); struct switchdev_notifier_vxlan_fdb_info *fdb_info);
int vxlan_fdb_replay(const struct net_device *dev, __be32 vni, int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
struct notifier_block *nb); struct notifier_block *nb,
struct netlink_ext_ack *extack);
void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni); void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni);
#else #else
@ -440,7 +441,8 @@ vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
} }
static inline int vxlan_fdb_replay(const struct net_device *dev, __be32 vni, static inline int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
struct notifier_block *nb) struct notifier_block *nb,
struct netlink_ext_ack *extack)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -915,7 +915,8 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
/* Add new permanent fdb entry with RTM_NEWNEIGH */ /* Add new permanent fdb entry with RTM_NEWNEIGH */
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlh_flags) const unsigned char *addr, u16 vid, u16 nlh_flags,
struct netlink_ext_ack *extack)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_port *p = NULL; struct net_bridge_port *p = NULL;

View File

@ -573,7 +573,8 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, const unsigned char *addr, u16 vid); struct net_device *dev, const unsigned char *addr, u16 vid);
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlh_flags); const unsigned char *addr, u16 vid, u16 nlh_flags,
struct netlink_ext_ack *extack);
int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev, struct net_device *fdev, int *idx); struct net_device *dev, struct net_device *fdev, int *idx);
int br_fdb_get(struct sk_buff *skb, struct nlattr *tb[], struct net_device *dev, int br_fdb_get(struct sk_buff *skb, struct nlattr *tb[], struct net_device *dev,

View File

@ -113,7 +113,7 @@ br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
info.added_by_user = added_by_user; info.added_by_user = added_by_user;
info.offloaded = offloaded; info.offloaded = offloaded;
notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
call_switchdev_notifiers(notifier_type, dev, &info.info); call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
} }
void void

View File

@ -3639,7 +3639,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
const struct net_device_ops *ops = br_dev->netdev_ops; const struct net_device_ops *ops = br_dev->netdev_ops;
err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid, err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid,
nlh->nlmsg_flags); nlh->nlmsg_flags, extack);
if (err) if (err)
goto out; goto out;
else else
@ -3651,7 +3651,8 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
if (dev->netdev_ops->ndo_fdb_add) if (dev->netdev_ops->ndo_fdb_add)
err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr, err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
vid, vid,
nlh->nlmsg_flags); nlh->nlmsg_flags,
extack);
else else
err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid,
nlh->nlmsg_flags); nlh->nlmsg_flags);

View File

@ -103,7 +103,8 @@ static inline void dsa_legacy_unregister(void) { }
int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, const unsigned char *addr, u16 vid,
u16 flags); u16 flags,
struct netlink_ext_ack *extack);
int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid);

View File

@ -1009,7 +1009,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid, const unsigned char *addr, u16 vid,
u16 flags) u16 flags,
struct netlink_ext_ack *extack)
{ {
struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_port *dp = dsa_slave_to_port(dev);
@ -1450,7 +1451,7 @@ static void dsa_slave_switchdev_event_work(struct work_struct *work)
} }
fdb_info->offloaded = true; fdb_info->offloaded = true;
call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
&fdb_info->info); &fdb_info->info, NULL);
break; break;
case SWITCHDEV_FDB_DEL_TO_DEVICE: case SWITCHDEV_FDB_DEL_TO_DEVICE:

View File

@ -556,10 +556,11 @@ EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
* Call all network notifier blocks. * Call all network notifier blocks.
*/ */
int call_switchdev_notifiers(unsigned long val, struct net_device *dev, int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
struct switchdev_notifier_info *info) struct switchdev_notifier_info *info,
struct netlink_ext_ack *extack)
{ {
info->dev = dev; info->dev = dev;
info->extack = NULL; info->extack = extack;
return atomic_notifier_call_chain(&switchdev_notif_chain, val, info); return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
} }
EXPORT_SYMBOL_GPL(call_switchdev_notifiers); EXPORT_SYMBOL_GPL(call_switchdev_notifiers);

View File

@ -0,0 +1,126 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test vetoing of FDB entries that mlxsw can not offload. This exercises several
# different veto vectors to test various rollback scenarios in the vxlan driver.
lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
fdb_create_veto_test
fdb_replace_veto_test
fdb_append_veto_test
fdb_changelink_veto_test
"
NUM_NETIFS=2
source $lib_dir/lib.sh
setup_prepare()
{
swp1=${NETIFS[p1]}
swp2=${NETIFS[p2]}
ip link add dev br0 type bridge mcast_snooping 0
ip link set dev $swp1 up
ip link set dev $swp1 master br0
ip link set dev $swp2 up
ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
ttl 20 tos inherit local 198.51.100.1 dstport 4789
ip link set dev vxlan0 master br0
}
cleanup()
{
pre_cleanup
ip link set dev vxlan0 nomaster
ip link del dev vxlan0
ip link set dev $swp2 down
ip link set dev $swp1 nomaster
ip link set dev $swp1 down
ip link del dev br0
}
fdb_create_veto_test()
{
RET=0
bridge fdb add 01:02:03:04:05:06 dev vxlan0 self static \
dst 198.51.100.2 2>/dev/null
check_fail $? "multicast MAC not rejected"
bridge fdb add 01:02:03:04:05:06 dev vxlan0 self static \
dst 198.51.100.2 2>&1 >/dev/null | grep -q mlxsw_spectrum
check_err $? "multicast MAC rejected without extack"
log_test "vxlan FDB veto - create"
}
fdb_replace_veto_test()
{
RET=0
bridge fdb add 00:01:02:03:04:05 dev vxlan0 self static \
dst 198.51.100.2
check_err $? "valid FDB rejected"
bridge fdb replace 00:01:02:03:04:05 dev vxlan0 self static \
dst 198.51.100.2 port 1234 2>/dev/null
check_fail $? "FDB with an explicit port not rejected"
bridge fdb replace 00:01:02:03:04:05 dev vxlan0 self static \
dst 198.51.100.2 port 1234 2>&1 >/dev/null \
| grep -q mlxsw_spectrum
check_err $? "FDB with an explicit port rejected without extack"
log_test "vxlan FDB veto - replace"
}
fdb_append_veto_test()
{
RET=0
bridge fdb add 00:00:00:00:00:00 dev vxlan0 self static \
dst 198.51.100.2
check_err $? "valid FDB rejected"
bridge fdb append 00:00:00:00:00:00 dev vxlan0 self static \
dst 198.51.100.3 port 1234 2>/dev/null
check_fail $? "FDB with an explicit port not rejected"
bridge fdb append 00:00:00:00:00:00 dev vxlan0 self static \
dst 198.51.100.3 port 1234 2>&1 >/dev/null \
| grep -q mlxsw_spectrum
check_err $? "FDB with an explicit port rejected without extack"
log_test "vxlan FDB veto - append"
}
fdb_changelink_veto_test()
{
RET=0
ip link set dev vxlan0 type vxlan \
group 224.0.0.1 dev lo 2>/dev/null
check_fail $? "FDB with a multicast IP not rejected"
ip link set dev vxlan0 type vxlan \
group 224.0.0.1 dev lo 2>&1 >/dev/null \
| grep -q mlxsw_spectrum
check_err $? "FDB with a multicast IP rejected without extack"
log_test "vxlan FDB veto - changelink"
}
trap cleanup EXIT
setup_prepare
setup_wait
tests_run
exit $EXIT_STATUS