OSDN Git Service

mlxsw: spectrum_span: Support mirror to gretap
authorPetr Machata <petrm@mellanox.com>
Tue, 27 Feb 2018 13:53:48 +0000 (14:53 +0100)
committerDavid S. Miller <davem@davemloft.net>
Tue, 27 Feb 2018 19:46:28 +0000 (14:46 -0500)
When a user requests mirror from a mlxsw physical port (possibly based
on an ACL match) to a gretap netdevice, the driver needs to resolve the
request to a particular physical port that the mirrored packets will
egress through, and a suite of configuration keys (importantly, IP and
MAC addresses). That means calling into routing and neighbor kernel code
to simulate the decisions made by the system for packets passing through
a gretap netdevice.

Add a new instance of mlxsw_sp_span_entry_ops to support this.

Signed-off-by: Petr Machata <petrm@mellanox.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/Kconfig
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h

index d56eea3..830c3e2 100644 (file)
@@ -78,6 +78,8 @@ config MLXSW_SPECTRUM
        depends on IPV6 || IPV6=n
        select PARMAN
        select MLXFW
+       depends on NET_IPGRE
+       depends on !(MLXSW_CORE=y && NET_IPGRE=m)
        default m
        ---help---
          This driver supports Mellanox Technologies Spectrum Ethernet
index 57df57c..d5fda4f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * 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:
  */
 
 #include <linux/list.h>
+#include <net/arp.h>
+#include <net/gre.h>
 
 #include "spectrum.h"
 #include "spectrum_span.h"
+#include "spectrum_ipip.h"
 
 int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
 {
@@ -127,17 +131,176 @@ struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
        .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 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,
 };
 
 static int
 mlxsw_sp_span_entry_nop_parms(const struct net_device *to_dev,
                              struct mlxsw_sp_span_parms *sparmsp)
 {
-       sparmsp->dest_port = NULL;
-       return 0;
+       return mlxsw_sp_span_entry_unoffloadable(sparmsp);
 }
 
 static int
index b6dcd7d..948aceb 100644 (file)
@@ -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;
@@ -52,6 +55,11 @@ struct mlxsw_sp_span_inspected_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;