This patch extends the vswitchd schema to allow specifying settings
specific to mirror hardware offload. The settings include:
mirror-offload, flow-dst-addr, bond-port-other, mirror-tunnel-addr,
output-src-vlan, and output-dst-vlan.  The mirror-offload setting is a
boolean variable that can be used to enable mirror offloading. The
flow-dst-addr variable allows for filtering of traffic to be mirrored
based on the destination MAC address. The mirror-tunnel-addr variable
enables mirroring to ports attached to bridges other than the traffic
source bridge. The output-src-vlan and the output-dst-vlan are used
to specify the VLAN tag for ingress and egress traffic. This allows
a VLAN tag to be added to mirrored traffic, which is used to trigger
device mirroring by means of the VLAN tag. The added VLAN tag can be used
to denote the source of the mirrored traffic. The added tag can be 
stripped if the destination VNF has VLAN stripping enabled.
An example of configuring egress traffic mirroring with VLAN tag 100 
is as follows:
ovs-vsctl -- set bridge br-int mirror=@m1 -- --id=@vnic1 get port
vnic1 -- --id=@m1 create mirror name=egress mirror-offload=1
select-src-port=@vnic1 mirror-tunnel-addr="0000\:87\:02.0"
output-src-vlan=100
An example of configuring ingress traffic mirroring with VLAN tag 200 
is as follows:
ovs-vsctl -- set bridge br-int mirror=@m2 -- --id=@vnic1 get port
vnic1 -- --id=@m2 create mirror name=ingress mirror-offload=1
select-dst-port=@vnic1 mirror-tunnel-addr="0000\:87\:02.1"
output-dst-vlan=200
An example of configuring per-flow egress traffic mirroring with VLAN tag
300 is as follows:
ovs-vsctl -- set bridge br-int mirror=@m3 -- --id=@vnic1 get port
vnic1-- --id=@m3 create mirror name=perflow mirror-offload=1
select-src-port=@vnic1 flow-dst-mac="52\:54\:00\:00\:00\:01"
mirror-tunnel-addr="0000\:18\:00.0" output-src-vlan=300
Note: Users can use the default ovs-vsctl commands to inspect
and remove the mirror configuration settings.

Signed-off-by: Liang-Min Wang <liang-min.w...@intel.com
Tested-by: Timothy Miskell <timothy.misk...@intel.com>
Suggested-by: Munish Mehan <mm6...@att.com>
---
 vswitchd/bridge.c          | 268 ++++++++++++++++++++++++++++++++++++-
 vswitchd/vswitch.ovsschema |  24 +++-
 vswitchd/vswitch.xml       |  54 ++++++++
 3 files changed, 342 insertions(+), 4 deletions(-)

diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 5ed7e8234..a7f903d9c 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -38,6 +38,7 @@
 #include "mac-learning.h"
 #include "mcast-snooping.h"
 #include "netdev.h"
+#include "netdev-provider.h"
 #include "netdev-offload.h"
 #include "nx-match.h"
 #include "ofproto/bond.h"
@@ -330,6 +331,9 @@ static void mirror_destroy(struct mirror *);
 static bool mirror_configure(struct mirror *);
 static void mirror_refresh_stats(struct mirror *);
 
+static void mirror_offload_destroy(struct mirror *);
+static bool mirror_offload_configure(struct mirror *);
+
 static void iface_configure_lacp(struct iface *,
                                  struct lacp_member_settings *);
 static bool iface_create(struct bridge *, const struct ovsrec_interface *,
@@ -423,6 +427,35 @@ if_notifier_changed(struct if_notifier *notifier 
OVS_UNUSED)
     seq_wait(ifaces_changed, last_ifaces_changed);
     return changed;
 }
+
+static struct port *
+port_lookup_all(const char *port_name)
+{
+    struct bridge *br;
+    struct port *port = NULL;
+    int found = 0;
+
+    HMAP_FOR_EACH (br, node, &all_bridges) {
+        struct port *temp_port = NULL;
+        temp_port = port_lookup(br, port_name);
+        if (temp_port) {
+            if (!port) {
+                port = temp_port;
+            }
+            found++;
+        }
+    }
+
+    if (found) {
+        if (found > 1) {
+            VLOG_INFO("More than one bridge owns port with name:%s\n",
+                port_name);
+        }
+        return port;
+    }
+    return NULL;
+}
+
 
 /* Public functions. */
 
@@ -5055,14 +5088,225 @@ mirror_create(struct bridge *br, const struct 
ovsrec_mirror *cfg)
     return m;
 }
 
+static struct netdev *get_netdev_from_port(struct mirror *m,
+    struct port **port, const char *name)
+{
+    struct port *temp_port;
+    struct iface *iface;
+
+    *port = NULL;
+    temp_port = port_lookup(m->bridge, name);
+    if (temp_port) {
+        LIST_FOR_EACH (iface, port_elem, &temp_port->ifaces) {
+            if (iface) {
+                *port = temp_port;
+                return iface->netdev;
+            }
+        }
+    }
+    /* try different bridges */
+    temp_port = port_lookup_all(name);
+    if (temp_port) {
+        LIST_FOR_EACH (iface, port_elem, &temp_port->ifaces) {
+            if (iface) {
+                *port = temp_port;
+                return iface->netdev;
+            }
+        }
+    }
+    return NULL;
+}
+
+static void
+release_mirror_offload_info(struct mirror_offload_info *info)
+{
+    if (info->src) {
+        free(info->src);
+    }
+    if (info->dst) {
+        free(info->dst);
+    }
+    if (info->flow_dst_mac) {
+        free(info->flow_dst_mac);
+    }
+    if (info->flow_src_mac) {
+        free(info->flow_src_mac);
+    }
+    if (info->output_src_tags) {
+        free(info->output_src_tags);
+    }
+    if (info->output_dst_tags) {
+        free(info->output_dst_tags);
+    }
+    if (info->name) {
+        free(info->name);
+    }
+    if (info->mirror_tunnel_addr) {
+        free(info->mirror_tunnel_addr);
+    }
+}
+
+static int
+set_mirror_offload_info(struct mirror *m, struct mirror_offload_info *info)
+{
+    const struct ovsrec_mirror *cfg = m->cfg;
+    struct port *port = NULL;
+    int i;
+
+    if (m->name) {
+        info->name = xmalloc(strlen(m->name)+1);
+        ovs_strzcpy(info->name, m->name, strlen(m->name));
+    }
+
+    if (cfg->mirror_tunnel_addr) {
+        info->mirror_tunnel_addr = xmalloc(strlen(cfg->mirror_tunnel_addr)
+            + 1);
+        ovs_strzcpy(info->mirror_tunnel_addr, cfg->mirror_tunnel_addr, 
strlen(cfg->mirror_tunnel_addr));
+    } else {
+        VLOG_ERR("mirror-offload configuration fails because"
+            " lack of tunnel device\n");
+        return -1;
+    }
+
+    /* source port */
+    info->n_src_port = cfg->n_select_src_port;
+    if (info->n_src_port) {
+        info->src = xmalloc(sizeof(struct netdev *)*info->n_src_port);
+        info->flow_dst_mac = xmalloc(sizeof(struct eth_addr)*
+            info->n_src_port);
+        if (info->n_src_port != cfg->n_output_src_vlan) {
+            VLOG_ERR("src port count:%d ouput src vlan count:%ld",
+                info->n_src_port, cfg->n_output_src_vlan);
+            return -1;
+        }
+        info->output_src_tags = xmalloc(sizeof(uint16_t)*info->n_src_port);
+    }
+
+    if (info->n_src_port) {
+        /* find netdev instance for each port */
+        for (i = 0; i < info->n_src_port; i++) {
+            info->src[i] = get_netdev_from_port(m, &port,
+                cfg->select_src_port[i]->name);
+            if (!info->src[i]) {
+                VLOG_ERR("src-port: %s is not a netdev device\n",
+                    cfg->select_src_port[i]->name);
+                return -1;
+            }
+        }
+        memset(info->flow_dst_mac, 0, sizeof(struct eth_addr)*
+            info->n_src_port);
+
+        /*
+         * for source port, flow is separated by
+         * different dst mac addr
+         */
+        if (cfg->n_flow_dst_mac) {
+            int dst_count = (info->n_src_port > cfg->n_flow_dst_mac)?
+                cfg->n_flow_dst_mac:info->n_src_port;
+            for (i = 0; i < dst_count; i++) {
+                eth_addr_from_string(cfg->flow_dst_mac[i],
+                    &info->flow_dst_mac[i]);
+            }
+        }
+
+        if (cfg->n_output_src_vlan) {
+            int count = (cfg->n_output_src_vlan > info->n_src_port)?
+                info->n_src_port:cfg->n_output_src_vlan;
+            for (i = 0; i < count; i++)
+                info->output_src_tags[i] = cfg->output_src_vlan[i] & 0xFFF;
+        }
+    }
+
+    /* dst ports */
+    info->n_dst_port = cfg->n_select_dst_port;
+    if (info->n_dst_port) {
+        info->dst = xmalloc(sizeof(struct netdev *)*info->n_dst_port);
+        info->flow_src_mac = xmalloc(sizeof(struct eth_addr)*
+            info->n_dst_port);
+        if (info->n_dst_port != cfg->n_output_dst_vlan) {
+            VLOG_ERR("dst port count:%d ouput dst vlan count:%ld\n",
+                info->n_dst_port, cfg->n_output_dst_vlan);
+            return -1;
+        }
+        info->output_dst_tags = xmalloc(sizeof(uint16_t)*info->n_dst_port);
+    }
+
+    if (info->n_dst_port) {
+        for (i = 0; i < info->n_dst_port; i++) {
+            info->dst[i] = get_netdev_from_port(m, &port,
+                cfg->select_dst_port[i]->name);
+            if (!info->dst[i]) {
+                VLOG_ERR("dst-port: %s is not a netdev device\n",
+                    cfg->select_dst_port[i]->name);
+                return -1;
+            }
+        }
+        memset(info->flow_src_mac, 0, sizeof(struct eth_addr)*
+            info->n_dst_port);
+
+        /*
+         * for destination port, flow is separated by
+         * different src mac addr
+         */
+        if (cfg->n_flow_src_mac) {
+            int src_count = (info->n_dst_port > cfg->n_flow_src_mac)?
+                cfg->n_flow_src_mac:info->n_dst_port;
+            for (i = 0; i < src_count; i++) {
+                eth_addr_from_string(cfg->flow_src_mac[i],
+                    &info->flow_src_mac[i]);
+            }
+        }
+
+        if (cfg->n_output_dst_vlan) {
+            int count = (cfg->n_output_dst_vlan > info->n_dst_port)?
+                info->n_dst_port:cfg->n_output_dst_vlan;
+            for (i = 0; i < count; i++)
+                info->output_dst_tags[i] = cfg->output_dst_vlan[i] & 0xFFF;
+        }
+    }
+
+    VLOG_INFO("sucess creating mirror-offload(%s): with %d src-port"
+        " streams %d dst-port streams to tunnel %s\n",
+        cfg->name, info->n_src_port, info->n_dst_port,
+        info->mirror_tunnel_addr?info->mirror_tunnel_addr:"none");
+    return 0;
+}
+
+static void
+mirror_offload_destroy(struct mirror *m)
+{
+    struct mirror_offload_info info;
+
+    memset(&info, 0, sizeof(struct mirror_offload_info));
+    info.add_mirror = false;
+    if (m->name) {
+        info.name = malloc(strlen(m->name)+1);
+        if (info.name) {
+            ovs_strzcpy(info.name, m->name, strlen(m->name));
+        }
+    }
+
+    netdev_mirror_offload_put(&info);
+    if (info.name) {
+        free(info.name);
+    }
+    if (info.mirror_tunnel_addr) {
+        free(info.mirror_tunnel_addr);
+    }
+}
+
 static void
 mirror_destroy(struct mirror *m)
 {
     if (m) {
         struct bridge *br = m->bridge;
 
-        if (br->ofproto) {
-            ofproto_mirror_unregister(br->ofproto, m);
+        if (m->cfg && m->cfg->mirror_offload) {
+            mirror_offload_destroy(m);
+        } else {
+            if (br->ofproto) {
+                ofproto_mirror_unregister(br->ofproto, m);
+            }
         }
 
         hmap_remove(&br->mirrors, &m->hmap_node);
@@ -5094,12 +5338,32 @@ mirror_collect_ports(struct mirror *m,
     *n_out_portsp = n_out_ports;
 }
 
+static bool
+mirror_offload_configure(struct mirror *m)
+{
+    struct mirror_offload_info info;
+
+    memset(&info, 0, sizeof(struct mirror_offload_info));
+    info.add_mirror = true;
+    if (set_mirror_offload_info(m, &info)) {
+        release_mirror_offload_info(&info);
+        return false;
+    }
+
+    netdev_mirror_offload_put(&info);
+    release_mirror_offload_info(&info);
+    return true;
+}
+
 static bool
 mirror_configure(struct mirror *m)
 {
     const struct ovsrec_mirror *cfg = m->cfg;
     struct ofproto_mirror_settings s;
 
+    if (cfg->mirror_offload) {
+        return mirror_offload_configure(m);
+    }
     /* Set name. */
     if (strcmp(cfg->name, m->name)) {
         free(m->name);
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 0666c8c76..4a1a34a1f 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "8.2.0",
- "cksum": "1076640191 26427",
+ "version": "8.2.1",
+ "cksum": "4051567316 27206",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -418,8 +418,18 @@
      "columns": {
        "name": {
          "type": "string"},
+       "mirror_tunnel_addr": {
+         "type": "string"},
        "select_all": {
          "type": "boolean"},
+       "mirror_offload": {
+         "type": "boolean"},
+       "flow_src_mac": {
+         "type": {"key": {"type": "string"},
+                  "min": 0, "max": "unlimited"}},
+       "flow_dst_mac": {
+         "type": {"key": {"type": "string"},
+                  "min": 0, "max": "unlimited"}},
        "select_src_port": {
          "type": {"key": {"type": "uuid",
                           "refTable": "Port",
@@ -440,6 +450,16 @@
                           "refTable": "Port",
                           "refType": "weak"},
                   "min": 0, "max": 1}},
+       "output_src_vlan": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4294967295},
+                  "min": 0, "max": 4096}},
+       "output_dst_vlan": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4294967295},
+                  "min": 0, "max": 4096}},
        "output_vlan": {
          "type": {"key": {"type": "integer",
                           "minInteger": 1,
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 4597a215d..6f3ff2f1f 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -4869,11 +4869,37 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 
type=patch options:peer=p1 \
         selected VLANs.
       </p>
 
+      <column name="mirror_tunnel_addr">
+        BDF string of the tunnel device on which mirrored traffic will be
+        transmitted. This parameter is only supported in OVS-DPDK.
+      </column>
+
       <column name="select_all">
         If true, every packet arriving or departing on any port is
         selected for mirroring.
       </column>
 
+      <column name="mirror_offload">
+        If true, a hw-assisted port mirroring is configured instead
+        default mirroring. This parameter is only supported in OVS-DPDK
+      </column>
+
+      <column name="flow_src_mac">
+        The source MAC address(es) for per-flow mirroring. Each MAC
+        address is separate by ','. This parametr is paired with
+        select_dst_port. A '0' MAC address indicates the requested mirror
+        is a per-port mirroring, otherwise it's a per-flow mirroring.
+        This parameter is only supported in OVS-DPDK.
+      </column>
+
+      <column name="flow_dst_mac">
+        The destination MAC address(es) for per-flow mirroring. Each MAC
+        address is separate by ','. This parametr is paired with
+        select_src_port. A '0' MAC address indicates the requested mirror
+        is a per-port mirroring, otherwise it's a per-flow mirroring.
+        This parameter is only supported in OVS-DPDK.
+      </column>
+
       <column name="select_dst_port">
         Ports on which departing packets are selected for mirroring.
       </column>
@@ -4955,6 +4981,34 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch 
options:peer=p1 \
         </p>
       </column>
 
+      <column name="output_src_vlan">
+        <p>Output VLAN for selected source port packets, if nonempty.</p>
+        <p>
+          <em>Please note:</em> This parametr is only supported in OVS-DPDK,
+          and this parameter is different than
+          <ref column="output-vlan"/> This vlan is used to add an additional
+          vlan tag on the mirror traffic, regardless it contains vlan or not.
+          The receive end could choose to filter out this additional vlan.
+          This option is provided so the mirrored traffic could maintain its
+          original vlan informaiton, and this mirror can be used to filter
+          out un-wanted traffic such as in <ref column="mirror_offload"/>.
+        </p>
+      </column>
+
+      <column name="output_dst_vlan">
+        <p>Output VLAN for selected destination port packets, if nonempty.</p>
+        <p>
+          <em>Please note:</em> This parametr is only supported in OVS-DPDK,
+          and this parameter is different than
+          <ref column="output-vlan"/> This vlan is used to add an additional
+          vlan tag on the mirror traffic, regardless it contains vlan or not.
+          The receive end could choose to filter out this additional vlan.
+          This option is provided so the mirrored traffic could maintain its
+          original vlan informaiton, and this mirror cab be used to filter
+          out un-wanted traffic such as in <ref column="mirror_offload"/>.
+        </p>
+      </column>
+
       <column name="snaplen">
         <p>Maximum per-packet number of bytes to mirror.</p>
         <p>A mirrored packet with size larger than <ref column="snaplen"/>
-- 
2.17.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to