Commit 431c7ec3 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Offloading-encapsulated-SPAN'

Jiri Pirko says:

====================
mlxsw: Offloading encapsulated SPAN

Petr says:

This patch series introduces support for mirroring with GRE
encapsulation. It offloads tc action mirred mirror from a mlxsw port to
either a gretap or an ip6gretap netdevice.

Spectrum hardware needs to know all the details of the requested
encapsulation: source and destination MAC and IP addresses, details of
VLAN tagging, etc. The only variables are the encapsulated packet
itself, and TOS field, which may be inherited. To that end, mlxsw driver
resolves the route that encapsulated packets would take, queries the
corresponding neighbor, and with that configuration in hand, configures
the mirroring in the hardware.

The driver also hooks into event handlers for netdevice changes, FIB and
neighbor events, and reconsiders the configuration on each such change.
When the new configuration differs from the currently-offloaded one, the
existing offload is removed and replaced with a new one.

It is possible to mirror to {ip6,}gretap from a matchall rule as well as
from a flower match.

** Note that with this patch set, mlxsw build depends on NET_IPGRE and
   IPV6_GRE.

Current limitations:

- There has to be a route that directs packets to an mlxsw port. We
  intend to extend the logic to support other netdevice types in the
  future, but the eventual egress netdevice will have to be an mlxsw
  port in any case.

- Offload reconfiguration due to changes in netdevice configuration
  creates a window of time where packets are not mirrored. Under some
  circumstances this can be prevented by configuring an unused port
  analyzer and migrating mirrors over to that. However that's currently
  not implemented.

- Remote address of a tunnel device needs to be set, there may not be a
  GRE key, checksumming or sequence numbers, and TTL needs to be fixed
  (non-inherit). These are hard requirements imposed by the underlying
  hardware.

- TOS of a tunnel device needs to be "inherit". The hardware supports a
  fixed TOS, but that's currently not implemented.

The series start with two patches, #1 and #2, that publish one function
and add support for querying IPv6 tunnel parameters.

In patches #3 and #4, we introduce helpers to GRE and tunneling code
that we will use later in the patchset from the SPAN code.

Patches #5 and #6 introduce support for encapsulated SPAN in reg.h.

The following seven patches, #7-#13, then prepare the SPAN codebase for
introduction of mirroring to netdevices that don't correspond to front
panel ports.

Then #14 and #15 pull all this together to implement mirroring to
{ip6,}gretap netdevices.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 63d63801 8f08a528
......@@ -78,6 +78,10 @@ config MLXSW_SPECTRUM
depends on IPV6 || IPV6=n
select PARMAN
select MLXFW
depends on NET_IPGRE
depends on !(MLXSW_CORE=y && NET_IPGRE=m)
depends on IPV6_GRE
depends on !(MLXSW_CORE=y && IPV6_GRE=m)
default m
---help---
This driver supports Mellanox Technologies Spectrum Ethernet
......
/*
* drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017, 2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Jiri Pirko <jiri@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
......@@ -838,7 +838,6 @@ struct mlxsw_afa_mirror {
struct mlxsw_afa_resource resource;
int span_id;
u8 local_in_port;
u8 local_out_port;
bool ingress;
};
......@@ -848,7 +847,7 @@ mlxsw_afa_mirror_destroy(struct mlxsw_afa_block *block,
{
block->afa->ops->mirror_del(block->afa->ops_priv,
mirror->local_in_port,
mirror->local_out_port,
mirror->span_id,
mirror->ingress);
kfree(mirror);
}
......@@ -864,9 +863,8 @@ mlxsw_afa_mirror_destructor(struct mlxsw_afa_block *block,
}
static struct mlxsw_afa_mirror *
mlxsw_afa_mirror_create(struct mlxsw_afa_block *block,
u8 local_in_port, u8 local_out_port,
bool ingress)
mlxsw_afa_mirror_create(struct mlxsw_afa_block *block, u8 local_in_port,
const struct net_device *out_dev, bool ingress)
{
struct mlxsw_afa_mirror *mirror;
int err;
......@@ -876,13 +874,12 @@ mlxsw_afa_mirror_create(struct mlxsw_afa_block *block,
return ERR_PTR(-ENOMEM);
err = block->afa->ops->mirror_add(block->afa->ops_priv,
local_in_port, local_out_port,
local_in_port, out_dev,
ingress, &mirror->span_id);
if (err)
goto err_mirror_add;
mirror->ingress = ingress;
mirror->local_out_port = local_out_port;
mirror->local_in_port = local_in_port;
mirror->resource.destructor = mlxsw_afa_mirror_destructor;
mlxsw_afa_resource_add(block, &mirror->resource);
......@@ -909,13 +906,13 @@ mlxsw_afa_block_append_allocated_mirror(struct mlxsw_afa_block *block,
}
int
mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
u8 local_in_port, u8 local_out_port, bool ingress)
mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block, u8 local_in_port,
const struct net_device *out_dev, bool ingress)
{
struct mlxsw_afa_mirror *mirror;
int err;
mirror = mlxsw_afa_mirror_create(block, local_in_port, local_out_port,
mirror = mlxsw_afa_mirror_create(block, local_in_port, out_dev,
ingress);
if (IS_ERR(mirror))
return PTR_ERR(mirror);
......
......@@ -36,6 +36,7 @@
#define _MLXSW_CORE_ACL_FLEX_ACTIONS_H
#include <linux/types.h>
#include <linux/netdevice.h>
struct mlxsw_afa;
struct mlxsw_afa_block;
......@@ -48,9 +49,10 @@ struct mlxsw_afa_ops {
void (*kvdl_fwd_entry_del)(void *priv, u32 kvdl_index);
int (*counter_index_get)(void *priv, unsigned int *p_counter_index);
void (*counter_index_put)(void *priv, unsigned int counter_index);
int (*mirror_add)(void *priv, u8 locol_in_port, u8 local_out_port,
int (*mirror_add)(void *priv, u8 local_in_port,
const struct net_device *out_dev,
bool ingress, int *p_span_id);
void (*mirror_del)(void *priv, u8 locol_in_port, u8 local_out_port,
void (*mirror_del)(void *priv, u8 local_in_port, int span_id,
bool ingress);
};
......@@ -70,7 +72,8 @@ int mlxsw_afa_block_append_trap(struct mlxsw_afa_block *block, u16 trap_id);
int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
u16 trap_id);
int mlxsw_afa_block_append_mirror(struct mlxsw_afa_block *block,
u8 local_in_port, u8 local_out_port,
u8 local_in_port,
const struct net_device *out_dev,
bool ingress);
int mlxsw_afa_block_append_fwd(struct mlxsw_afa_block *block,
u8 local_port, bool in_port);
......
/*
* drivers/net/ethernet/mellanox/mlxsw/reg.h
* Copyright (c) 2015-2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2016 Ido Schimmel <idosch@mellanox.com>
* Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
* Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
* Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
* Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
......@@ -6772,8 +6772,104 @@ MLXSW_ITEM32(reg, mpat, qos, 0x04, 26, 1);
*/
MLXSW_ITEM32(reg, mpat, be, 0x04, 25, 1);
enum mlxsw_reg_mpat_span_type {
/* Local SPAN Ethernet.
* The original packet is not encapsulated.
*/
MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH = 0x0,
/* Encapsulated Remote SPAN Ethernet L3 GRE.
* The packet is encapsulated with GRE header.
*/
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3 = 0x3,
};
/* reg_mpat_span_type
* SPAN type.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, span_type, 0x04, 0, 4);
/* Remote SPAN - Ethernet VLAN
* - - - - - - - - - - - - - -
*/
/* reg_mpat_eth_rspan_vid
* Encapsulation header VLAN ID.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_vid, 0x18, 0, 12);
/* Encapsulated Remote SPAN - Ethernet L2
* - - - - - - - - - - - - - - - - - - -
*/
enum mlxsw_reg_mpat_eth_rspan_version {
MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER = 15,
};
/* reg_mpat_eth_rspan_version
* RSPAN mirror header version.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_version, 0x10, 18, 4);
/* reg_mpat_eth_rspan_mac
* Destination MAC address.
* Access: RW
*/
MLXSW_ITEM_BUF(reg, mpat, eth_rspan_mac, 0x12, 6);
/* reg_mpat_eth_rspan_tp
* Tag Packet. Indicates whether the mirroring header should be VLAN tagged.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_tp, 0x18, 16, 1);
/* Encapsulated Remote SPAN - Ethernet L3
* - - - - - - - - - - - - - - - - - - -
*/
enum mlxsw_reg_mpat_eth_rspan_protocol {
MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV4,
MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV6,
};
/* reg_mpat_eth_rspan_protocol
* SPAN encapsulation protocol.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_protocol, 0x18, 24, 4);
/* reg_mpat_eth_rspan_ttl
* Encapsulation header Time-to-Live/HopLimit.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_ttl, 0x1C, 4, 8);
/* reg_mpat_eth_rspan_smac
* Source MAC address
* Access: RW
*/
MLXSW_ITEM_BUF(reg, mpat, eth_rspan_smac, 0x22, 6);
/* reg_mpat_eth_rspan_dip*
* Destination IP address. The IP version is configured by protocol.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_dip4, 0x4C, 0, 32);
MLXSW_ITEM_BUF(reg, mpat, eth_rspan_dip6, 0x40, 16);
/* reg_mpat_eth_rspan_sip*
* Source IP address. The IP version is configured by protocol.
* Access: RW
*/
MLXSW_ITEM32(reg, mpat, eth_rspan_sip4, 0x5C, 0, 32);
MLXSW_ITEM_BUF(reg, mpat, eth_rspan_sip6, 0x50, 16);
static inline void mlxsw_reg_mpat_pack(char *payload, u8 pa_id,
u16 system_port, bool e)
u16 system_port, bool e,
enum mlxsw_reg_mpat_span_type span_type)
{
MLXSW_REG_ZERO(mpat, payload);
mlxsw_reg_mpat_pa_id_set(payload, pa_id);
......@@ -6781,6 +6877,49 @@ static inline void mlxsw_reg_mpat_pack(char *payload, u8 pa_id,
mlxsw_reg_mpat_e_set(payload, e);
mlxsw_reg_mpat_qos_set(payload, 1);
mlxsw_reg_mpat_be_set(payload, 1);
mlxsw_reg_mpat_span_type_set(payload, span_type);
}
static inline void mlxsw_reg_mpat_eth_rspan_pack(char *payload, u16 vid)
{
mlxsw_reg_mpat_eth_rspan_vid_set(payload, vid);
}
static inline void
mlxsw_reg_mpat_eth_rspan_l2_pack(char *payload,
enum mlxsw_reg_mpat_eth_rspan_version version,
const char *mac,
bool tp)
{
mlxsw_reg_mpat_eth_rspan_version_set(payload, version);
mlxsw_reg_mpat_eth_rspan_mac_memcpy_to(payload, mac);
mlxsw_reg_mpat_eth_rspan_tp_set(payload, tp);
}
static inline void
mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(char *payload, u8 ttl,
const char *smac,
u32 sip, u32 dip)
{
mlxsw_reg_mpat_eth_rspan_ttl_set(payload, ttl);
mlxsw_reg_mpat_eth_rspan_smac_memcpy_to(payload, smac);
mlxsw_reg_mpat_eth_rspan_protocol_set(payload,
MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV4);
mlxsw_reg_mpat_eth_rspan_sip4_set(payload, sip);
mlxsw_reg_mpat_eth_rspan_dip4_set(payload, dip);
}
static inline void
mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(char *payload, u8 ttl,
const char *smac,
struct in6_addr sip, struct in6_addr dip)
{
mlxsw_reg_mpat_eth_rspan_ttl_set(payload, ttl);
mlxsw_reg_mpat_eth_rspan_smac_memcpy_to(payload, smac);
mlxsw_reg_mpat_eth_rspan_protocol_set(payload,
MLXSW_REG_MPAT_ETH_RSPAN_PROTOCOL_IPV6);
mlxsw_reg_mpat_eth_rspan_sip6_memcpy_to(payload, (void *)&sip);
mlxsw_reg_mpat_eth_rspan_dip6_memcpy_to(payload, (void *)&dip);
}
/* MPAR - Monitoring Port Analyzer Register
......
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum.c
* Copyright (c) 2015-2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
* Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
......@@ -1258,7 +1258,6 @@ mlxsw_sp_port_add_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
bool ingress)
{
enum mlxsw_sp_span_type span_type;
struct mlxsw_sp_port *to_port;
struct net_device *to_dev;
to_dev = tcf_mirred_dev(a);
......@@ -1267,17 +1266,10 @@ mlxsw_sp_port_add_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
return -EINVAL;
}
if (!mlxsw_sp_port_dev_check(to_dev)) {
netdev_err(mlxsw_sp_port->dev, "Cannot mirror to a non-spectrum port");
return -EOPNOTSUPP;
}
to_port = netdev_priv(to_dev);
mirror->to_local_port = to_port->local_port;
mirror->ingress = ingress;
span_type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
return mlxsw_sp_span_mirror_add(mlxsw_sp_port, to_port, span_type,
true);
return mlxsw_sp_span_mirror_add(mlxsw_sp_port, to_dev, span_type,
true, &mirror->span_id);
}
static void
......@@ -1288,7 +1280,7 @@ mlxsw_sp_port_del_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
span_type = mirror->ingress ?
MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
mlxsw_sp_span_mirror_del(mlxsw_sp_port, mirror->to_local_port,
mlxsw_sp_span_mirror_del(mlxsw_sp_port, mirror->span_id,
span_type, true);
}
......@@ -3675,14 +3667,24 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_afa_init;
}
err = mlxsw_sp_span_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to init span system\n");
goto err_span_init;
}
/* Initialize router after SPAN is initialized, so that the FIB and
* neighbor event handlers can issue SPAN respin.
*/
err = mlxsw_sp_router_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n");
goto err_router_init;
}
/* Initialize netdevice notifier after router is initialized, so that
* the event handler can use router structures.
/* Initialize netdevice notifier after router and SPAN is initialized,
* so that the event handler can use router structures and call SPAN
* respin.
*/
mlxsw_sp->netdevice_nb.notifier_call = mlxsw_sp_netdevice_event;
err = register_netdevice_notifier(&mlxsw_sp->netdevice_nb);
......@@ -3691,12 +3693,6 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_netdev_notifier;
}
err = mlxsw_sp_span_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to init span system\n");
goto err_span_init;
}
err = mlxsw_sp_acl_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
......@@ -3722,12 +3718,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
err_dpipe_init:
mlxsw_sp_acl_fini(mlxsw_sp);
err_acl_init:
mlxsw_sp_span_fini(mlxsw_sp);
err_span_init:
unregister_netdevice_notifier(&mlxsw_sp->netdevice_nb);
err_netdev_notifier:
mlxsw_sp_router_fini(mlxsw_sp);
err_router_init:
mlxsw_sp_span_fini(mlxsw_sp);
err_span_init:
mlxsw_sp_afa_fini(mlxsw_sp);
err_afa_init:
mlxsw_sp_counter_pool_fini(mlxsw_sp);
......@@ -3753,9 +3749,9 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
mlxsw_sp_ports_remove(mlxsw_sp);
mlxsw_sp_dpipe_fini(mlxsw_sp);
mlxsw_sp_acl_fini(mlxsw_sp);
mlxsw_sp_span_fini(mlxsw_sp);
unregister_netdevice_notifier(&mlxsw_sp->netdevice_nb);
mlxsw_sp_router_fini(mlxsw_sp);
mlxsw_sp_span_fini(mlxsw_sp);
mlxsw_sp_afa_fini(mlxsw_sp);
mlxsw_sp_counter_pool_fini(mlxsw_sp);
mlxsw_sp_switchdev_fini(mlxsw_sp);
......@@ -4639,10 +4635,18 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct mlxsw_sp_span_entry *span_entry;
struct mlxsw_sp *mlxsw_sp;
int err = 0;
mlxsw_sp = container_of(nb, struct mlxsw_sp, netdevice_nb);
if (event == NETDEV_UNREGISTER) {
span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, dev);
if (span_entry)
mlxsw_sp_span_entry_invalidate(mlxsw_sp, span_entry);
}
mlxsw_sp_span_respin(mlxsw_sp);
if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))
err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,
event, ptr);
......
......@@ -124,7 +124,7 @@ enum mlxsw_sp_port_mall_action_type {
};
struct mlxsw_sp_port_mall_mirror_tc_entry {
u8 to_local_port;
int span_id;
bool ingress;
};
......
......@@ -572,7 +572,6 @@ int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
struct net_device *out_dev)
{
struct mlxsw_sp_acl_block_binding *binding;
struct mlxsw_sp_port *out_port;
struct mlxsw_sp_port *in_port;
if (!list_is_singular(&block->binding_list))
......@@ -581,16 +580,10 @@ int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
binding = list_first_entry(&block->binding_list,
struct mlxsw_sp_acl_block_binding, list);
in_port = binding->mlxsw_sp_port;
if (!mlxsw_sp_port_dev_check(out_dev))
return -EINVAL;
out_port = netdev_priv(out_dev);
if (out_port->mlxsw_sp != mlxsw_sp)
return -EINVAL;
return mlxsw_afa_block_append_mirror(rulei->act_block,
in_port->local_port,
out_port->local_port,
out_dev,
binding->ingress);
}
......
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017, 2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Jiri Pirko <jiri@mellanox.com>
* Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com>
*
......@@ -126,40 +126,23 @@ mlxsw_sp_act_counter_index_put(void *priv, unsigned int counter_index)
}
static int
mlxsw_sp_act_mirror_add(void *priv, u8 local_in_port, u8 local_out_port,
mlxsw_sp_act_mirror_add(void *priv, u8 local_in_port,
const struct net_device *out_dev,
bool ingress, int *p_span_id)
{
struct mlxsw_sp_port *in_port, *out_port;
struct mlxsw_sp_span_entry *span_entry;
struct mlxsw_sp_port *in_port;
struct mlxsw_sp *mlxsw_sp = priv;
enum mlxsw_sp_span_type type;
int err;
type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
out_port = mlxsw_sp->ports[local_out_port];
in_port = mlxsw_sp->ports[local_in_port];
err = mlxsw_sp_span_mirror_add(in_port, out_port, type, false);
if (err)
return err;
span_entry = mlxsw_sp_span_entry_find(mlxsw_sp, local_out_port);
if (!span_entry) {
err = -ENOENT;
goto err_span_entry_find;
}
*p_span_id = span_entry->id;
return 0;
err_span_entry_find:
mlxsw_sp_span_mirror_del(in_port, local_out_port, type, false);
return err;
return mlxsw_sp_span_mirror_add(in_port, out_dev, type,
false, p_span_id);
}
static void
mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, u8 local_out_port,
bool ingress)
mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, int span_id, bool ingress)
{
struct mlxsw_sp *mlxsw_sp = priv;
struct mlxsw_sp_port *in_port;
......@@ -168,7 +151,7 @@ mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, u8 local_out_port,
type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
in_port = mlxsw_sp->ports[local_in_port];
mlxsw_sp_span_mirror_del(in_port, local_out_port, type, false);
mlxsw_sp_span_mirror_del(in_port, span_id, type, false);
}
static const struct mlxsw_afa_ops mlxsw_sp_act_afa_ops = {
......
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
......@@ -33,6 +33,7 @@
*/
#include <net/ip_tunnels.h>
#include <net/ip6_tunnel.h>
#include "spectrum_ipip.h"
......@@ -44,6 +45,14 @@ mlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev)
return tun->parms;
}
struct __ip6_tnl_parm
mlxsw_sp_ipip_netdev_parms6(const struct net_device *ol_dev)
{
struct ip6_tnl *tun = netdev_priv(ol_dev);
return tun->parms;
}
static bool mlxsw_sp_ipip_parms4_has_ikey(struct ip_tunnel_parm parms)
{
return !!(parms.i_flags & TUNNEL_KEY);
......@@ -72,24 +81,38 @@ mlxsw_sp_ipip_parms4_saddr(struct ip_tunnel_parm parms)
return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.saddr };
}
static union mlxsw_sp_l3addr
mlxsw_sp_ipip_parms6_saddr(struct __ip6_tnl_parm parms)
{
return (union mlxsw_sp_l3addr) { .addr6 = parms.laddr };
}
static union mlxsw_sp_l3addr
mlxsw_sp_ipip_parms4_daddr(struct ip_tunnel_parm parms)
{
return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.daddr };
}
static union mlxsw_sp_l3addr
mlxsw_sp_ipip_parms6_daddr(struct __ip6_tnl_parm parms)
{
return (union mlxsw_sp_l3addr) { .addr6 = parms.raddr };
}
union mlxsw_sp_l3addr
mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
const struct net_device *ol_dev)
{
struct ip_tunnel_parm parms4;
struct __ip6_tnl_parm parms6;
switch (proto) {
case MLXSW_SP_L3_PROTO_IPV4:
parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
return mlxsw_sp_ipip_parms4_saddr(parms4);
case MLXSW_SP_L3_PROTO_IPV6:
break;
parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev);
return mlxsw_sp_ipip_parms6_saddr(parms6);
}
WARN_ON(1);
......@@ -109,19 +132,28 @@ mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
const struct net_device *ol_dev)
{
struct ip_tunnel_parm parms4;
struct __ip6_tnl_parm parms6;
switch (proto) {
case MLXSW_SP_L3_PROTO_IPV4:
parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
return mlxsw_sp_ipip_parms4_daddr(parms4);
case MLXSW_SP_L3_PROTO_IPV6:
break;
parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev);
return mlxsw_sp_ipip_parms6_daddr(parms6);
}
WARN_ON(1);
return (union mlxsw_sp_l3addr) {0};
}
bool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr)
{
union mlxsw_sp_l3addr naddr = {0};
return !memcmp(&addr, &naddr, sizeof(naddr));
}
static int
mlxsw_sp_ipip_nexthop_update_gre4(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
struct mlxsw_sp_ipip_entry *ipip_entry)
......@@ -215,15 +247,14 @@ static bool mlxsw_sp_ipip_tunnel_complete(enum mlxsw_sp_l3proto proto,
{
union mlxsw_sp_l3addr saddr = mlxsw_sp_ipip_netdev_saddr(proto, ol_dev);
union mlxsw_sp_l3addr daddr = mlxsw_sp_ipip_netdev_daddr(proto, ol_dev);
union mlxsw_sp_l3addr naddr = {0};
/* Tunnels with unset local or remote address are valid in Linux and
* used for lightweight tunnels (LWT) and Non-Broadcast Multi-Access
* (NBMA) tunnels. In principle these can be offloaded, but the driver
* currently doesn't support this. So punt.
*/
return memcmp(&saddr, &naddr, sizeof(naddr)) &&
memcmp(&daddr, &naddr, sizeof(naddr));
return !mlxsw_sp_l3addr_is_zero(saddr) &&
!mlxsw_sp_l3addr_is_zero(daddr);
}
static bool mlxsw_sp_ipip_can_offload_gre4(const struct mlxsw_sp *mlxsw_sp,
......
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2017-2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
......@@ -41,11 +41,15 @@
struct ip_tunnel_parm
mlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev);
struct __ip6_tnl_parm
mlxsw_sp_ipip_netdev_parms6(const struct net_device *ol_dev);
union mlxsw_sp_l3addr
mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
const struct net_device *ol_dev);
bool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr);
enum mlxsw_sp_ipip_type {
MLXSW_SP_IPIP_TYPE_GRE4,
MLXSW_SP_IPIP_TYPE_MAX,
......
......@@ -70,6 +70,7 @@
#include "spectrum_mr.h"
#include "spectrum_mr_tcam.h"
#include "spectrum_router.h"
#include "spectrum_span.h"
struct mlxsw_sp_fib;
struct mlxsw_sp_vr;
......@@ -2330,6 +2331,8 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
read_unlock_bh(&n->lock);
rtnl_lock();
mlxsw_sp_span_respin(mlxsw_sp);
entry_connected = nud_state & NUD_VALID && !dead;
neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
if (!entry_connected && !neigh_entry)
......@@ -5589,6 +5592,8 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
/* Protect internal structures from changes */
rtnl_lock();
mlxsw_sp_span_respin(mlxsw_sp);
switch (fib_work->event) {
case FIB_EVENT_ENTRY_REPLACE: /* fall through */
case FIB_EVENT_ENTRY_APPEND: /* fall through */
......@@ -5631,6 +5636,8 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
int err;
rtnl_lock();
mlxsw_sp_span_respin(mlxsw_sp);
switch (fib_work->event) {
case FIB_EVENT_ENTRY_REPLACE: /* fall through */
case FIB_EVENT_ENTRY_ADD:
......
/*
* drivers/net/ethernet/mellanox/mlxsw/mlxsw_span.c
* Copyright (c) 2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2018 Petr Machata <petrm@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
......@@ -32,9 +33,14 @@
*/
#include <linux/list.h>
#include <net/arp.h>
#include <net/gre.h>
#include <net/ndisc.h>
#include <net/ip6_tunnel.h>
#include "spectrum.h"
#include "spectrum_span.h"
#include "spectrum_ipip.h"
int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
{
......@@ -51,8 +57,12 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp->span.entries)
return -ENOMEM;
for (i = 0; i < mlxsw_sp->span.entries_count; i++)
INIT_LIST_HEAD(&mlxsw_sp->span.entries[i].bound_ports_list);
for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
INIT_LIST_HEAD(&curr->bound_ports_list);
curr->id = i;
}
return 0;
}
......@@ -69,80 +79,460 @@ void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
kfree(mlxsw_sp->span.entries);
}
static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_create(struct mlxsw_sp_port *port)
static int
mlxsw_sp_span_entry_phys_parms(const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
struct mlxsw_sp_span_entry *span_entry;
sparmsp->dest_port = netdev_priv(to_dev);
return 0;
}
static int
mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms)
{
struct mlxsw_sp_port *dest_port = sparms.dest_port;
struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
u8 local_port = dest_port->local_port;
char mpat_pl[MLXSW_REG_MPAT_LEN];
int pa_id = span_entry->id;
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
}
static void
mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
enum mlxsw_reg_mpat_span_type span_type)
{
struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port;
struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
u8 local_port = dest_port->local_port;
char mpat_pl[MLXSW_REG_MPAT_LEN];
int pa_id = span_entry->id;
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
}
static void
mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
{
mlxsw_sp_span_entry_deconfigure_common(span_entry,
MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
}
static const
struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
.can_handle = mlxsw_sp_port_dev_check,
.parms = mlxsw_sp_span_entry_phys_parms,
.configure = mlxsw_sp_span_entry_phys_configure,
.deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
};
static struct net_device *
mlxsw_sp_span_gretap4_route(const struct net_device *to_dev,
__be32 *saddrp, __be32 *daddrp)
{
struct ip_tunnel *tun = netdev_priv(to_dev);
struct net_device *dev = NULL;
struct ip_tunnel_parm parms;
struct rtable *rt = NULL;
struct flowi4 fl4;
/* We assume "dev" stays valid after rt is put. */
ASSERT_RTNL();
parms = mlxsw_sp_ipip_netdev_parms4(to_dev);
ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp,
0, 0, parms.link, tun->fwmark);
rt = ip_route_output_key(tun->net, &fl4);
if (IS_ERR(rt))
return NULL;
if (rt->rt_type != RTN_UNICAST)
goto out;
dev = rt->dst.dev;
*saddrp = fl4.saddr;
*daddrp = rt->rt_gateway;
out:
ip_rt_put(rt);
return dev;
}
static int mlxsw_sp_span_dmac(struct neigh_table *tbl,
const void *pkey,
struct net_device *l3edev,
unsigned char dmac[ETH_ALEN])
{
struct neighbour *neigh = neigh_lookup(tbl, pkey, l3edev);
int err = 0;
if (!neigh) {
neigh = neigh_create(tbl, pkey, l3edev);
if (IS_ERR(neigh))
return PTR_ERR(neigh);
}
neigh_event_send(neigh, NULL);
read_lock_bh(&neigh->lock);
if ((neigh->nud_state & NUD_VALID) && !neigh->dead)
memcpy(dmac, neigh->ha, ETH_ALEN);
else
err = -ENOENT;
read_unlock_bh(&neigh->lock);
neigh_release(neigh);
return err;
}
static int
mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp)
{
sparmsp->dest_port = NULL;
return 0;
}
static int
mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *l3edev,
union mlxsw_sp_l3addr saddr,
union mlxsw_sp_l3addr daddr,
union mlxsw_sp_l3addr gw,
__u8 ttl,
struct neigh_table *tbl,
struct mlxsw_sp_span_parms *sparmsp)
{
unsigned char dmac[ETH_ALEN];
if (mlxsw_sp_l3addr_is_zero(gw))
gw = daddr;
if (!l3edev || !mlxsw_sp_port_dev_check(l3edev) ||
mlxsw_sp_span_dmac(tbl, &gw, l3edev, dmac))
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
sparmsp->dest_port = netdev_priv(l3edev);
sparmsp->ttl = ttl;
memcpy(sparmsp->dmac, dmac, ETH_ALEN);
memcpy(sparmsp->smac, l3edev->dev_addr, ETH_ALEN);
sparmsp->saddr = saddr;
sparmsp->daddr = daddr;
return 0;
}
static int
mlxsw_sp_span_entry_gretap4_parms(const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr };
union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr };
bool inherit_tos = tparm.iph.tos & 0x1;
bool inherit_ttl = !tparm.iph.ttl;
union mlxsw_sp_l3addr gw = daddr;
struct net_device *l3edev;
if (!(to_dev->flags & IFF_UP) ||
/* Reject tunnels with GRE keys, checksums, etc. */
tparm.i_flags || tparm.o_flags ||
/* Require a fixed TTL and a TOS copied from the mirrored packet. */
inherit_ttl || !inherit_tos ||
/* A destination address may not be "any". */
mlxsw_sp_l3addr_is_zero(daddr))
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
tparm.iph.ttl,
&arp_tbl, sparmsp);
}
static int
mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms)
{
struct mlxsw_sp_port *dest_port = sparms.dest_port;
struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
u8 local_port = dest_port->local_port;
char mpat_pl[MLXSW_REG_MPAT_LEN];
int pa_id = span_entry->id;
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
sparms.dmac, false);
mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl,
sparms.ttl, sparms.smac,
be32_to_cpu(sparms.saddr.addr4),
be32_to_cpu(sparms.daddr.addr4));
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
}
static void
mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
{
mlxsw_sp_span_entry_deconfigure_common(span_entry,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
}
static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
.can_handle = is_gretap_dev,
.parms = mlxsw_sp_span_entry_gretap4_parms,
.configure = mlxsw_sp_span_entry_gretap4_configure,
.deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
};
static struct net_device *
mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
struct in6_addr *saddrp,
struct in6_addr *daddrp)
{
struct ip6_tnl *t = netdev_priv(to_dev);
struct flowi6 fl6 = t->fl.u.ip6;
struct net_device *dev = NULL;
struct dst_entry *dst;
struct rt6_info *rt6;
/* We assume "dev" stays valid after dst is released. */
ASSERT_RTNL();
fl6.flowi6_mark = t->parms.fwmark;
if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr))
return NULL;
dst = ip6_route_output(t->net, NULL, &fl6);
if (!dst || dst->error)
goto out;
rt6 = container_of(dst, struct rt6_info, dst);
dev = dst->dev;
*saddrp = fl6.saddr;
*daddrp = rt6->rt6i_gateway;
out:
dst_release(dst);
return dev;
}
static int
mlxsw_sp_span_entry_gretap6_parms(const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr };
union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
bool inherit_ttl = !tparm.hop_limit;
union mlxsw_sp_l3addr gw = daddr;
struct net_device *l3edev;
if (!(to_dev->flags & IFF_UP) ||
/* Reject tunnels with GRE keys, checksums, etc. */
tparm.i_flags || tparm.o_flags ||
/* Require a fixed TTL and a TOS copied from the mirrored packet. */
inherit_ttl || !inherit_tos ||
/* A destination address may not be "any". */
mlxsw_sp_l3addr_is_zero(daddr))
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
tparm.hop_limit,
&nd_tbl, sparmsp);
}
static int
mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms)
{
struct mlxsw_sp_port *dest_port = sparms.dest_port;
struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
u8 local_port = dest_port->local_port;
char mpat_pl[MLXSW_REG_MPAT_LEN];
u8 local_port = port->local_port;
int index;
int pa_id = span_entry->id;
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
sparms.dmac, false);
mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac,
sparms.saddr.addr6,
sparms.daddr.addr6);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
}
static void
mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
{
mlxsw_sp_span_entry_deconfigure_common(span_entry,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
}
static const
struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
.can_handle = is_ip6gretap_dev,
.parms = mlxsw_sp_span_entry_gretap6_parms,
.configure = mlxsw_sp_span_entry_gretap6_configure,
.deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
};
static const
struct mlxsw_sp_span_entry_ops *const mlxsw_sp_span_entry_types[] = {
&mlxsw_sp_span_entry_ops_phys,
&mlxsw_sp_span_entry_ops_gretap4,
&mlxsw_sp_span_entry_ops_gretap6,
};
static int
mlxsw_sp_span_entry_nop_parms(const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
}
static int
mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms)
{
return 0;
}
static void
mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
{
}
static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
.parms = mlxsw_sp_span_entry_nop_parms,
.configure = mlxsw_sp_span_entry_nop_configure,
.deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
};
static void
mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms)
{
if (sparms.dest_port) {
if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
netdev_err(span_entry->to_dev, "Cannot mirror to %s, which belongs to a different mlxsw instance",
sparms.dest_port->dev->name);
sparms.dest_port = NULL;
} else if (span_entry->ops->configure(span_entry, sparms)) {
netdev_err(span_entry->to_dev, "Failed to offload mirror to %s",
sparms.dest_port->dev->name);
sparms.dest_port = NULL;
}
}
span_entry->parms = sparms;
}
static void
mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
{
if (span_entry->parms.dest_port)
span_entry->ops->deconfigure(span_entry);
}
static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev,
const struct mlxsw_sp_span_entry_ops *ops,
struct mlxsw_sp_span_parms sparms)
{
struct mlxsw_sp_span_entry *span_entry = NULL;
int i;
int err;
/* find a free entry to use */
index = -1;
for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
if (!mlxsw_sp->span.entries[i].ref_count) {
index = i;
span_entry = &mlxsw_sp->span.entries[i];
break;
}
}
if (index < 0)
return NULL;
/* create a new port analayzer entry for local_port */
mlxsw_reg_mpat_pack(mpat_pl, index, local_port, true);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
if (err)
if (!span_entry)
return NULL;
span_entry->id = index;
span_entry->ops = ops;
span_entry->ref_count = 1;
span_entry->local_port = local_port;
span_entry->to_dev = to_dev;
mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
return span_entry;
}
static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_span_entry *span_entry)
static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp_span_entry *span_entry)
{
u8 local_port = span_entry->local_port;
char mpat_pl[MLXSW_REG_MPAT_LEN];
int pa_id = span_entry->id;
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
mlxsw_sp_span_entry_deconfigure(span_entry);
}
struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find(struct mlxsw_sp *mlxsw_sp, u8 local_port)
mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev)
{
int i;
for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
if (curr->ref_count && curr->local_port == local_port)
if (curr->ref_count && curr->to_dev == to_dev)
return curr;
}
return NULL;
}
void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_span_entry *span_entry)
{
mlxsw_sp_span_entry_deconfigure(span_entry);
span_entry->ops = &mlxsw_sp_span_entry_ops_nop;
}
static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_get(struct mlxsw_sp_port *port)
mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
{
int i;
for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
if (curr->ref_count && curr->id == span_id)
return curr;
}
return NULL;
}
static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev,
const struct mlxsw_sp_span_entry_ops *ops,
struct mlxsw_sp_span_parms sparms)
{
struct mlxsw_sp_span_entry *span_entry;
span_entry = mlxsw_sp_span_entry_find(port->mlxsw_sp,
port->local_port);
span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, to_dev);
if (span_entry) {
/* Already exists, just take a reference */
span_entry->ref_count++;
return span_entry;
}
return mlxsw_sp_span_entry_create(port);
return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms);
}
static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
......@@ -150,7 +540,7 @@ static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
{
WARN_ON(!span_entry->ref_count);
if (--span_entry->ref_count == 0)
mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
mlxsw_sp_span_entry_destroy(span_entry);
return 0;
}
......@@ -312,15 +702,41 @@ mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
kfree(inspected_port);
}
static const struct mlxsw_sp_span_entry_ops *
mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(mlxsw_sp_span_entry_types); ++i)
if (mlxsw_sp_span_entry_types[i]->can_handle(to_dev))
return mlxsw_sp_span_entry_types[i];
return NULL;
}
int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
struct mlxsw_sp_port *to,
enum mlxsw_sp_span_type type, bool bind)
const struct net_device *to_dev,
enum mlxsw_sp_span_type type, bool bind,
int *p_span_id)
{
struct mlxsw_sp *mlxsw_sp = from->mlxsw_sp;
const struct mlxsw_sp_span_entry_ops *ops;
struct mlxsw_sp_span_parms sparms = {0};
struct mlxsw_sp_span_entry *span_entry;
int err;
span_entry = mlxsw_sp_span_entry_get(to);
ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
if (!ops) {
netdev_err(to_dev, "Cannot mirror to %s", to_dev->name);
return -EOPNOTSUPP;
}
err = ops->parms(to_dev, &sparms);
if (err)
return err;
span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
if (!span_entry)
return -ENOENT;
......@@ -331,6 +747,7 @@ int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
if (err)
goto err_port_bind;
*p_span_id = span_entry->id;
return 0;
err_port_bind:
......@@ -338,13 +755,12 @@ int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
return err;
}
void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, u8 destination_port,
void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
enum mlxsw_sp_span_type type, bool bind)
{
struct mlxsw_sp_span_entry *span_entry;
span_entry = mlxsw_sp_span_entry_find(from->mlxsw_sp,
destination_port);
span_entry = mlxsw_sp_span_entry_find_by_id(from->mlxsw_sp, span_id);
if (!span_entry) {
netdev_err(from->dev, "no span entry found\n");
return;
......@@ -354,3 +770,27 @@ void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, u8 destination_port,
span_entry->id);
mlxsw_sp_span_inspected_port_del(from, span_entry, type, bind);
}
void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
{
int i;
int err;
ASSERT_RTNL();
for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
struct mlxsw_sp_span_parms sparms = {0};
if (!curr->ref_count)
continue;
err = curr->ops->parms(curr->to_dev, &sparms);
if (err)
continue;
if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
mlxsw_sp_span_entry_deconfigure(curr);
mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
}
}
}
......@@ -35,6 +35,9 @@
#define _MLXSW_SPECTRUM_SPAN_H
#include <linux/types.h>
#include <linux/if_ether.h>
#include "spectrum_router.h"
struct mlxsw_sp;
struct mlxsw_sp_port;
......@@ -50,23 +53,51 @@ struct mlxsw_sp_span_inspected_port {
u8 local_port;
};
struct mlxsw_sp_span_parms {
struct mlxsw_sp_port *dest_port; /* NULL for unoffloaded SPAN. */
unsigned int ttl;
unsigned char dmac[ETH_ALEN];
unsigned char smac[ETH_ALEN];
union mlxsw_sp_l3addr daddr;
union mlxsw_sp_l3addr saddr;
};
struct mlxsw_sp_span_entry_ops;
struct mlxsw_sp_span_entry {
u8 local_port;
const struct net_device *to_dev;
const struct mlxsw_sp_span_entry_ops *ops;
struct mlxsw_sp_span_parms parms;
struct list_head bound_ports_list;
int ref_count;
int id;
};
struct mlxsw_sp_span_entry_ops {
bool (*can_handle)(const struct net_device *to_dev);
int (*parms)(const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp);
int (*configure)(struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms);
void (*deconfigure)(struct mlxsw_sp_span_entry *span_entry);
};
int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
struct mlxsw_sp_port *to,
enum mlxsw_sp_span_type type, bool bind);
void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, u8 destination_port,
const struct net_device *to_dev,
enum mlxsw_sp_span_type type,
bool bind, int *p_span_id);
void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
enum mlxsw_sp_span_type type, bool bind);
struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find(struct mlxsw_sp *mlxsw_sp, u8 local_port);
mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev);
void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_span_entry *span_entry);
int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu);
......
......@@ -37,6 +37,9 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
bool *csum_err, __be16 proto, int nhs);
bool is_gretap_dev(const struct net_device *dev);
bool is_ip6gretap_dev(const struct net_device *dev);
static inline int gre_calc_hlen(__be16 o_flags)
{
int addend = 4;
......
......@@ -254,6 +254,22 @@ static inline __be32 tunnel_id_to_key32(__be64 tun_id)
#ifdef CONFIG_INET
static inline void ip_tunnel_init_flow(struct flowi4 *fl4,
int proto,
__be32 daddr, __be32 saddr,
__be32 key, __u8 tos, int oif,
__u32 mark)
{
memset(fl4, 0, sizeof(*fl4));
fl4->flowi4_oif = oif;
fl4->daddr = daddr;
fl4->saddr = saddr;
fl4->flowi4_tos = tos;
fl4->flowi4_proto = proto;
fl4->fl4_gre_key = key;
fl4->flowi4_mark = mark;
}
int ip_tunnel_init(struct net_device *dev);
void ip_tunnel_uninit(struct net_device *dev);
void ip_tunnel_dellink(struct net_device *dev, struct list_head *head);
......
......@@ -1323,6 +1323,12 @@ static void ipgre_tap_setup(struct net_device *dev)
ip_tunnel_setup(dev, gre_tap_net_id);
}
bool is_gretap_dev(const struct net_device *dev)
{
return dev->netdev_ops == &gre_tap_netdev_ops;
}
EXPORT_SYMBOL_GPL(is_gretap_dev);
static int ipgre_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
......
......@@ -290,22 +290,6 @@ static struct net_device *__ip_tunnel_create(struct net *net,
return ERR_PTR(err);
}
static inline void init_tunnel_flow(struct flowi4 *fl4,
int proto,
__be32 daddr, __be32 saddr,
__be32 key, __u8 tos, int oif,
__u32 mark)
{
memset(fl4, 0, sizeof(*fl4));
fl4->flowi4_oif = oif;
fl4->daddr = daddr;
fl4->saddr = saddr;
fl4->flowi4_tos = tos;
fl4->flowi4_proto = proto;
fl4->fl4_gre_key = key;
fl4->flowi4_mark = mark;
}
static int ip_tunnel_bind_dev(struct net_device *dev)
{
struct net_device *tdev = NULL;
......@@ -322,10 +306,10 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
struct flowi4 fl4;
struct rtable *rt;
init_tunnel_flow(&fl4, iph->protocol, iph->daddr,
iph->saddr, tunnel->parms.o_key,
RT_TOS(iph->tos), tunnel->parms.link,
tunnel->fwmark);
ip_tunnel_init_flow(&fl4, iph->protocol, iph->daddr,
iph->saddr, tunnel->parms.o_key,
RT_TOS(iph->tos), tunnel->parms.link,
tunnel->fwmark);
rt = ip_route_output_key(tunnel->net, &fl4);
if (!IS_ERR(rt)) {
......@@ -581,8 +565,8 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, u8 proto)
else if (skb->protocol == htons(ETH_P_IPV6))
tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph);
}
init_tunnel_flow(&fl4, proto, key->u.ipv4.dst, key->u.ipv4.src, 0,
RT_TOS(tos), tunnel->parms.link, tunnel->fwmark);
ip_tunnel_init_flow(&fl4, proto, key->u.ipv4.dst, key->u.ipv4.src, 0,
RT_TOS(tos), tunnel->parms.link, tunnel->fwmark);
if (tunnel->encap.type != TUNNEL_ENCAP_NONE)
goto tx_error;
rt = ip_route_output_key(tunnel->net, &fl4);
......@@ -711,14 +695,14 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
}
if (tunnel->fwmark) {
init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr,
tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link,
tunnel->fwmark);
ip_tunnel_init_flow(&fl4, protocol, dst, tnl_params->saddr,
tunnel->parms.o_key, RT_TOS(tos),
tunnel->parms.link, tunnel->fwmark);
}
else {
init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr,
tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link,
skb->mark);
ip_tunnel_init_flow(&fl4, protocol, dst, tnl_params->saddr,
tunnel->parms.o_key, RT_TOS(tos),
tunnel->parms.link, skb->mark);
}
if (ip_tunnel_encap(skb, tunnel, &protocol, &fl4) < 0)
......
......@@ -1785,6 +1785,12 @@ static void ip6gre_tap_setup(struct net_device *dev)
netif_keep_dst(dev);
}
bool is_ip6gretap_dev(const struct net_device *dev)
{
return dev->netdev_ops == &ip6gre_tap_netdev_ops;
}
EXPORT_SYMBOL_GPL(is_ip6gretap_dev);
static bool ip6gre_netlink_encap_parms(struct nlattr *data[],
struct ip_tunnel_encap *ipencap)
{
......
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