Commit f5c64e56 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-VRRP'

Ido Schimmel says:

====================
mlxsw: Add VRRP support

When a router that is acting as the default gateway of a host stops
functioning, the host will encounter packet loss until the router starts
functioning again.

To increase the reliability of the default gateway without performing
reconfiguration on the host, a host can use a Virtual Router Redundancy
Protocol (VRRP) Router. This virtual router is composed from several
routers where only one is actually forwarding packets from the host (the
master router) while the other routers act as backup routers. The
election of the master router is determined by the VRRP protocol [1].

Packets addressed to the virtual router are always sent to the virtual
router MAC address (IPv4: 00-00-5E-00-01-XX, IPv6: 00-00-5E-00-02-XX).
Such packets can only be accepted by the master router and must be
discarded by the backup routers.

In Linux, VRRP is usually implemented by configuring a macvlan with the
virtual router MAC on top of the router interface that is connected to
the host / LAN. The macvlan on the master router is assigned the virtual
IP (VIP) that the host uses as its gateway.

In order to support VRRP in mlxsw, we first need to enable macvlan upper
devices on top of mlxsw netdevs and their uppers. This is done by the
first patch, which also takes care of sanitizing macvlan configurations
that are not currently supported by the driver.

The second patch directs packets with destination MAC addresses as the
macvlans to the router so that they will undergo an L3 lookup. This is
consistent with the kernel's behavior where the macvlan's Rx handler
will re-inject such packets to the Rx path so that they will be picked
up by the IPvX protocol handlers and undergo an L3 lookup. Note that the
driver prevents the macvlans from being enslaved to other devices, to
ensure the packets will be picked up by the protocol handler and not by
another Rx handler.

The third patch adds packet traps for VRRP control packets for both IPv4
and IPv6. Finally, the last patch optimizes the reception of VRRP MACs
by potentially skipping one L2 lookup for them.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ff0432e5 c3a49540
......@@ -4353,6 +4353,20 @@ MLXSW_ITEM32(reg, ritr, if_swid, 0x08, 24, 8);
*/
MLXSW_ITEM_BUF(reg, ritr, if_mac, 0x12, 6);
/* reg_ritr_if_vrrp_id_ipv6
* VRRP ID for IPv6
* Note: Reserved for RIF types other than VLAN, FID and Sub-port.
* Access: RW
*/
MLXSW_ITEM32(reg, ritr, if_vrrp_id_ipv6, 0x1C, 8, 8);
/* reg_ritr_if_vrrp_id_ipv4
* VRRP ID for IPv4
* Note: Reserved for RIF types other than VLAN, FID and Sub-port.
* Access: RW
*/
MLXSW_ITEM32(reg, ritr, if_vrrp_id_ipv4, 0x1C, 0, 8);
/* VLAN Interface */
/* reg_ritr_vlan_if_vid
......
......@@ -3386,6 +3386,8 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(IPIP_DECAP_ERROR, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(IPV4_VRRP, TRAP_TO_CPU, ROUTER_EXP, false),
MLXSW_SP_RXL_MARK(IPV6_VRRP, TRAP_TO_CPU, ROUTER_EXP, false),
/* PKT Sample trap */
MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU,
false, SP_IP2ME, DISCARD),
......@@ -4420,7 +4422,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
if (!is_vlan_dev(upper_dev) &&
!netif_is_lag_master(upper_dev) &&
!netif_is_bridge_master(upper_dev) &&
!netif_is_ovs_master(upper_dev)) {
!netif_is_ovs_master(upper_dev) &&
!netif_is_macvlan(upper_dev)) {
NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
return -EINVAL;
}
......@@ -4446,6 +4449,11 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on a LAG port");
return -EINVAL;
}
if (netif_is_macvlan(upper_dev) &&
!mlxsw_sp_rif_find_by_dev(mlxsw_sp, lower_dev)) {
NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
return -EOPNOTSUPP;
}
if (netif_is_ovs_master(upper_dev) && vlan_uses_dev(dev)) {
NL_SET_ERR_MSG_MOD(extack, "Master device is an OVS master and this device has a VLAN");
return -EINVAL;
......@@ -4484,6 +4492,9 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
err = mlxsw_sp_port_ovs_join(mlxsw_sp_port);
else
mlxsw_sp_port_ovs_leave(mlxsw_sp_port);
} else if (netif_is_macvlan(upper_dev)) {
if (!info->linking)
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
}
break;
}
......@@ -4568,8 +4579,9 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
switch (event) {
case NETDEV_PRECHANGEUPPER:
upper_dev = info->upper_dev;
if (!netif_is_bridge_master(upper_dev)) {
NL_SET_ERR_MSG_MOD(extack, "VLAN devices only support bridge and VRF uppers");
if (!netif_is_bridge_master(upper_dev) &&
!netif_is_macvlan(upper_dev)) {
NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
return -EINVAL;
}
if (!info->linking)
......@@ -4581,6 +4593,11 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
return -EINVAL;
}
if (netif_is_macvlan(upper_dev) &&
!mlxsw_sp_rif_find_by_dev(mlxsw_sp, vlan_dev)) {
NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
return -EOPNOTSUPP;
}
break;
case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
......@@ -4594,6 +4611,9 @@ static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
vlan_dev,
upper_dev);
} else if (netif_is_macvlan(upper_dev)) {
if (!info->linking)
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
} else {
err = -EINVAL;
WARN_ON(1);
......@@ -4643,6 +4663,64 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
return 0;
}
static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
unsigned long event, void *ptr)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(br_dev);
struct netdev_notifier_changeupper_info *info = ptr;
struct netlink_ext_ack *extack;
struct net_device *upper_dev;
if (!mlxsw_sp)
return 0;
extack = netdev_notifier_info_to_extack(&info->info);
switch (event) {
case NETDEV_PRECHANGEUPPER:
upper_dev = info->upper_dev;
if (!is_vlan_dev(upper_dev) && !netif_is_macvlan(upper_dev)) {
NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
return -EOPNOTSUPP;
}
if (!info->linking)
break;
if (netif_is_macvlan(upper_dev) &&
!mlxsw_sp_rif_find_by_dev(mlxsw_sp, br_dev)) {
NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
return -EOPNOTSUPP;
}
break;
case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
if (info->linking)
break;
if (netif_is_macvlan(upper_dev))
mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
break;
}
return 0;
}
static int mlxsw_sp_netdevice_macvlan_event(struct net_device *macvlan_dev,
unsigned long event, void *ptr)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
struct netdev_notifier_changeupper_info *info = ptr;
struct netlink_ext_ack *extack;
if (!mlxsw_sp || event != NETDEV_PRECHANGEUPPER)
return 0;
extack = netdev_notifier_info_to_extack(&info->info);
/* VRF enslavement is handled in mlxsw_sp_netdevice_vrf_event() */
NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
return -EOPNOTSUPP;
}
static bool mlxsw_sp_is_vrf_event(unsigned long event, void *ptr)
{
struct netdev_notifier_changeupper_info *info = ptr;
......@@ -4684,6 +4762,10 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
err = mlxsw_sp_netdevice_lag_event(dev, event, ptr);
else if (is_vlan_dev(dev))
err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr);
else if (netif_is_bridge_master(dev))
err = mlxsw_sp_netdevice_bridge_event(dev, event, ptr);
else if (netif_is_macvlan(dev))
err = mlxsw_sp_netdevice_macvlan_event(dev, event, ptr);
return notifier_from_errno(err);
}
......
......@@ -417,6 +417,8 @@ static inline void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port)
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);
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,
unsigned long event, void *ptr);
int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused,
......
......@@ -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 *
......
......@@ -63,6 +63,7 @@ enum {
MLXSW_TRAP_ID_LBERROR = 0x54,
MLXSW_TRAP_ID_IPV4_OSPF = 0x55,
MLXSW_TRAP_ID_IPV4_PIM = 0x58,
MLXSW_TRAP_ID_IPV4_VRRP = 0x59,
MLXSW_TRAP_ID_RPF = 0x5C,
MLXSW_TRAP_ID_IP2ME = 0x5F,
MLXSW_TRAP_ID_IPV6_UNSPECIFIED_ADDRESS = 0x60,
......@@ -78,6 +79,7 @@ enum {
MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F,
MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70,
MLXSW_TRAP_ID_IPV6_PIM = 0x79,
MLXSW_TRAP_ID_IPV6_VRRP = 0x7A,
MLXSW_TRAP_ID_IPV4_BGP = 0x88,
MLXSW_TRAP_ID_IPV6_BGP = 0x89,
MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment