|
@@ -48,6 +48,7 @@
|
|
|
#include <linux/route.h>
|
|
|
#include <linux/gcd.h>
|
|
|
#include <linux/random.h>
|
|
|
+#include <linux/if_macvlan.h>
|
|
|
#include <net/netevent.h>
|
|
|
#include <net/neighbour.h>
|
|
|
#include <net/arp.h>
|
|
@@ -60,6 +61,7 @@
|
|
|
#include <net/ndisc.h>
|
|
|
#include <net/ipv6.h>
|
|
|
#include <net/fib_notifier.h>
|
|
|
+#include <net/switchdev.h>
|
|
|
|
|
|
#include "spectrum.h"
|
|
|
#include "core.h"
|
|
@@ -165,6 +167,7 @@ struct mlxsw_sp_rif_ops {
|
|
|
void (*deconfigure)(struct mlxsw_sp_rif *rif);
|
|
|
struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif,
|
|
|
struct netlink_ext_ack *extack);
|
|
|
+ void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
|
|
|
};
|
|
|
|
|
|
static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree);
|
|
@@ -6027,6 +6030,12 @@ mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev,
|
|
|
!list_empty(&inet6_dev->addr_list))
|
|
|
addr_list_empty = false;
|
|
|
|
|
|
+ /* macvlans do not have a RIF, but rather piggy back on the
|
|
|
+ * RIF of their lower device.
|
|
|
+ */
|
|
|
+ if (netif_is_macvlan(dev) && addr_list_empty)
|
|
|
+ return true;
|
|
|
+
|
|
|
if (rif && addr_list_empty &&
|
|
|
!netif_is_l3_slave(rif->dev))
|
|
|
return true;
|
|
@@ -6440,6 +6449,123 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static bool mlxsw_sp_rif_macvlan_is_vrrp4(const u8 *mac)
|
|
|
+{
|
|
|
+ u8 vrrp4[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x01, 0x00 };
|
|
|
+ u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
|
|
+
|
|
|
+ return ether_addr_equal_masked(mac, vrrp4, mask);
|
|
|
+}
|
|
|
+
|
|
|
+static bool mlxsw_sp_rif_macvlan_is_vrrp6(const u8 *mac)
|
|
|
+{
|
|
|
+ u8 vrrp6[ETH_ALEN] = { 0x00, 0x00, 0x5e, 0x00, 0x02, 0x00 };
|
|
|
+ u8 mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
|
|
+
|
|
|
+ return ether_addr_equal_masked(mac, vrrp6, mask);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_rif_vrrp_op(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
|
|
|
+ const u8 *mac, bool adding)
|
|
|
+{
|
|
|
+ char ritr_pl[MLXSW_REG_RITR_LEN];
|
|
|
+ u8 vrrp_id = adding ? mac[5] : 0;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!mlxsw_sp_rif_macvlan_is_vrrp4(mac) &&
|
|
|
+ !mlxsw_sp_rif_macvlan_is_vrrp6(mac))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
|
|
|
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (mlxsw_sp_rif_macvlan_is_vrrp4(mac))
|
|
|
+ mlxsw_reg_ritr_if_vrrp_id_ipv4_set(ritr_pl, vrrp_id);
|
|
|
+ else
|
|
|
+ mlxsw_reg_ritr_if_vrrp_id_ipv6_set(ritr_pl, vrrp_id);
|
|
|
+
|
|
|
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_rif_macvlan_add(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ const struct net_device *macvlan_dev,
|
|
|
+ struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ struct macvlan_dev *vlan = netdev_priv(macvlan_dev);
|
|
|
+ struct mlxsw_sp_rif *rif;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);
|
|
|
+ if (!rif) {
|
|
|
+ NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
|
|
|
+ mlxsw_sp_fid_index(rif->fid), true);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index,
|
|
|
+ macvlan_dev->dev_addr, true);
|
|
|
+ if (err)
|
|
|
+ goto err_rif_vrrp_add;
|
|
|
+
|
|
|
+ /* Make sure the bridge driver does not have this MAC pointing at
|
|
|
+ * some other port.
|
|
|
+ */
|
|
|
+ if (rif->ops->fdb_del)
|
|
|
+ rif->ops->fdb_del(rif, macvlan_dev->dev_addr);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_rif_vrrp_add:
|
|
|
+ mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
|
|
|
+ mlxsw_sp_fid_index(rif->fid), false);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+void mlxsw_sp_rif_macvlan_del(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ const struct net_device *macvlan_dev)
|
|
|
+{
|
|
|
+ struct macvlan_dev *vlan = netdev_priv(macvlan_dev);
|
|
|
+ struct mlxsw_sp_rif *rif;
|
|
|
+
|
|
|
+ rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan->lowerdev);
|
|
|
+ /* If we do not have a RIF, then we already took care of
|
|
|
+ * removing the macvlan's MAC during RIF deletion.
|
|
|
+ */
|
|
|
+ if (!rif)
|
|
|
+ return;
|
|
|
+ mlxsw_sp_rif_vrrp_op(mlxsw_sp, rif->rif_index, macvlan_dev->dev_addr,
|
|
|
+ false);
|
|
|
+ mlxsw_sp_rif_fdb_op(mlxsw_sp, macvlan_dev->dev_addr,
|
|
|
+ mlxsw_sp_fid_index(rif->fid), false);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_inetaddr_macvlan_event(struct net_device *macvlan_dev,
|
|
|
+ unsigned long event,
|
|
|
+ struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ struct mlxsw_sp *mlxsw_sp;
|
|
|
+
|
|
|
+ mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
|
|
|
+ if (!mlxsw_sp)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case NETDEV_UP:
|
|
|
+ return mlxsw_sp_rif_macvlan_add(mlxsw_sp, macvlan_dev, extack);
|
|
|
+ case NETDEV_DOWN:
|
|
|
+ mlxsw_sp_rif_macvlan_del(mlxsw_sp, macvlan_dev);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
|
|
|
unsigned long event,
|
|
|
struct netlink_ext_ack *extack)
|
|
@@ -6452,6 +6578,8 @@ static int __mlxsw_sp_inetaddr_event(struct net_device *dev,
|
|
|
return mlxsw_sp_inetaddr_bridge_event(dev, event, extack);
|
|
|
else if (is_vlan_dev(dev))
|
|
|
return mlxsw_sp_inetaddr_vlan_event(dev, event, extack);
|
|
|
+ else if (netif_is_macvlan(dev))
|
|
|
+ return mlxsw_sp_inetaddr_macvlan_event(dev, event, extack);
|
|
|
else
|
|
|
return 0;
|
|
|
}
|
|
@@ -6692,7 +6820,10 @@ int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
|
|
|
int err = 0;
|
|
|
|
|
|
- if (!mlxsw_sp)
|
|
|
+ /* We do not create a RIF for a macvlan, but only use it to
|
|
|
+ * direct more MAC addresses to the router.
|
|
|
+ */
|
|
|
+ if (!mlxsw_sp || netif_is_macvlan(l3_dev))
|
|
|
return 0;
|
|
|
|
|
|
switch (event) {
|
|
@@ -6713,6 +6844,27 @@ int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int __mlxsw_sp_rif_macvlan_flush(struct net_device *dev, void *data)
|
|
|
+{
|
|
|
+ struct mlxsw_sp_rif *rif = data;
|
|
|
+
|
|
|
+ if (!netif_is_macvlan(dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, dev->dev_addr,
|
|
|
+ mlxsw_sp_fid_index(rif->fid), false);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_rif_macvlan_flush(struct mlxsw_sp_rif *rif)
|
|
|
+{
|
|
|
+ if (!netif_is_macvlan_port(rif->dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ netdev_warn(rif->dev, "Router interface is deleted. Upper macvlans will not work\n");
|
|
|
+ return netdev_walk_all_upper_dev_rcu(rif->dev,
|
|
|
+ __mlxsw_sp_rif_macvlan_flush, rif);
|
|
|
+}
|
|
|
+
|
|
|
static struct mlxsw_sp_rif_subport *
|
|
|
mlxsw_sp_rif_subport_rif(const struct mlxsw_sp_rif *rif)
|
|
|
{
|
|
@@ -6779,6 +6931,7 @@ static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif)
|
|
|
mlxsw_sp_fid_rif_set(fid, NULL);
|
|
|
mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
|
|
|
mlxsw_sp_fid_index(fid), false);
|
|
|
+ mlxsw_sp_rif_macvlan_flush(rif);
|
|
|
mlxsw_sp_rif_subport_op(rif, false);
|
|
|
}
|
|
|
|
|
@@ -6866,6 +7019,7 @@ static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif)
|
|
|
mlxsw_sp_fid_rif_set(fid, NULL);
|
|
|
mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
|
|
|
mlxsw_sp_fid_index(fid), false);
|
|
|
+ mlxsw_sp_rif_macvlan_flush(rif);
|
|
|
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
|
|
|
mlxsw_sp_router_port(mlxsw_sp), false);
|
|
|
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
|
|
@@ -6893,12 +7047,30 @@ mlxsw_sp_rif_vlan_fid_get(struct mlxsw_sp_rif *rif,
|
|
|
return mlxsw_sp_fid_8021q_get(rif->mlxsw_sp, vid);
|
|
|
}
|
|
|
|
|
|
+static void mlxsw_sp_rif_vlan_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
|
|
|
+{
|
|
|
+ u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid);
|
|
|
+ struct switchdev_notifier_fdb_info info;
|
|
|
+ struct net_device *br_dev;
|
|
|
+ struct net_device *dev;
|
|
|
+
|
|
|
+ br_dev = is_vlan_dev(rif->dev) ? vlan_dev_real_dev(rif->dev) : rif->dev;
|
|
|
+ dev = br_fdb_find_port(br_dev, mac, vid);
|
|
|
+ if (!dev)
|
|
|
+ return;
|
|
|
+
|
|
|
+ info.addr = mac;
|
|
|
+ info.vid = vid;
|
|
|
+ call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info);
|
|
|
+}
|
|
|
+
|
|
|
static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_ops = {
|
|
|
.type = MLXSW_SP_RIF_TYPE_VLAN,
|
|
|
.rif_size = sizeof(struct mlxsw_sp_rif),
|
|
|
.configure = mlxsw_sp_rif_vlan_configure,
|
|
|
.deconfigure = mlxsw_sp_rif_vlan_deconfigure,
|
|
|
.fid_get = mlxsw_sp_rif_vlan_fid_get,
|
|
|
+ .fdb_del = mlxsw_sp_rif_vlan_fdb_del,
|
|
|
};
|
|
|
|
|
|
static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif)
|
|
@@ -6950,6 +7122,7 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif)
|
|
|
mlxsw_sp_fid_rif_set(fid, NULL);
|
|
|
mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr,
|
|
|
mlxsw_sp_fid_index(fid), false);
|
|
|
+ mlxsw_sp_rif_macvlan_flush(rif);
|
|
|
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC,
|
|
|
mlxsw_sp_router_port(mlxsw_sp), false);
|
|
|
mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC,
|
|
@@ -6964,12 +7137,27 @@ mlxsw_sp_rif_fid_fid_get(struct mlxsw_sp_rif *rif,
|
|
|
return mlxsw_sp_fid_8021d_get(rif->mlxsw_sp, rif->dev->ifindex);
|
|
|
}
|
|
|
|
|
|
+static void mlxsw_sp_rif_fid_fdb_del(struct mlxsw_sp_rif *rif, const char *mac)
|
|
|
+{
|
|
|
+ struct switchdev_notifier_fdb_info info;
|
|
|
+ struct net_device *dev;
|
|
|
+
|
|
|
+ dev = br_fdb_find_port(rif->dev, mac, 0);
|
|
|
+ if (!dev)
|
|
|
+ return;
|
|
|
+
|
|
|
+ info.addr = mac;
|
|
|
+ info.vid = 0;
|
|
|
+ call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, dev, &info.info);
|
|
|
+}
|
|
|
+
|
|
|
static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
|
|
|
.type = MLXSW_SP_RIF_TYPE_FID,
|
|
|
.rif_size = sizeof(struct mlxsw_sp_rif),
|
|
|
.configure = mlxsw_sp_rif_fid_configure,
|
|
|
.deconfigure = mlxsw_sp_rif_fid_deconfigure,
|
|
|
.fid_get = mlxsw_sp_rif_fid_fid_get,
|
|
|
+ .fdb_del = mlxsw_sp_rif_fid_fdb_del,
|
|
|
};
|
|
|
|
|
|
static struct mlxsw_sp_rif_ipip_lb *
|