Hi Han,

For both patches in the series,

Acked-by: Mark Michelson <[email protected]>

I want to put a reply to patch 2 directly, but unfortunately I can't find it in my email. Sometimes patches get sent to spam, but in this case, it's not there either.

On 5/5/23 02:32, Han Zhou wrote:
Today the mirror feature in OVN supports only tunnel to a remote
destinations. This patch adds the support for mirroring to a local OVS
port. It is particularly useful for monitoring traffic that is offloaded
thus not possible to be intercepted by regular tools such as tcpdump.
With this feature, traffic to/from a virtual function that is offloaded
to hardware can be mirrored to a dedicated (pre-configured) OVS port
(which can be another virtual function that is dedicated for
interception purposes).

Signed-off-by: Han Zhou <[email protected]>
---
  NEWS                      |   1 +
  controller/mirror.c       | 134 ++++++++++++++++++++++++++++++++------
  ovn-nb.ovsschema          |   7 +-
  ovn-nb.xml                |  12 +++-
  ovn-sb.ovsschema          |   9 +--
  ovn-sb.xml                |  17 +++--
  tests/ovn.at              |  97 ++++++++++++++++++++++++++-
  utilities/ovn-nbctl.8.xml |  14 ++--
  utilities/ovn-nbctl.c     |  64 ++++++++++--------
  9 files changed, 287 insertions(+), 68 deletions(-)

diff --git a/NEWS b/NEWS
index 60467581a1ba..f49d4049c332 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Post v23.03.0
      existing behaviour of flooding these arp requests to all attached Ports.
    - Always allow IPv6 Router Discovery, Neighbor Discovery, and Multicast
      Listener Discovery protocols, regardless of ACLs defined.
+  - Support using local OVS port as port-mirroring target.
OVN v23.03.0 - 03 Mar 2023
  --------------------------
diff --git a/controller/mirror.c b/controller/mirror.c
index 6657369665fe..fd36c7650c41 100644
--- a/controller/mirror.c
+++ b/controller/mirror.c
@@ -54,10 +54,12 @@ static struct ovn_mirror *ovn_mirror_find(struct shash 
*ovn_mirrors,
  static void ovn_mirror_delete(struct ovn_mirror *);
  static void ovn_mirror_add_lport(struct ovn_mirror *, struct local_binding *);
  static void sync_ovn_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *,
-                            const struct ovsrec_bridge *);
+                            const struct ovsrec_bridge *,
+                            struct shash *ovs_mirror_ports);
static void create_ovs_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *,
-                              const struct ovsrec_bridge *);
+                              const struct ovsrec_bridge *,
+                              struct shash *ovs_mirror_ports);
  static void sync_ovs_mirror_ports(struct ovn_mirror *,
                                    const struct ovsrec_bridge *);
  static void delete_ovs_mirror(struct ovn_mirror *,
@@ -69,6 +71,8 @@ static void set_mirror_iface_options(struct ovsrec_interface 
*,
  static const struct ovsrec_port *get_iface_port(
      const struct ovsrec_interface *, const struct ovsrec_bridge *);
+static void build_ovs_mirror_ports(const struct ovsrec_bridge *,
+                                   struct shash *ovs_mirror_ports);
void
  mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl)
@@ -105,7 +109,8 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
      }
struct shash ovn_mirrors = SHASH_INITIALIZER(&ovn_mirrors);
-    struct shash tmp_mirrors = SHASH_INITIALIZER(&tmp_mirrors);
+    struct shash ovs_local_mirror_ports =
+        SHASH_INITIALIZER(&ovs_local_mirror_ports);
/* Iterate through sb mirrors and build the 'ovn_mirrors'. */
      const struct sbrec_mirror *sb_mirror;
@@ -137,6 +142,8 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
          return;
      }
+ build_ovs_mirror_ports(br_int, &ovs_local_mirror_ports);
+
      /* Iterate through the local bindings and if the local binding's 'pb' has
       * mirrors associated, add it to the ovn_mirror. */
      struct shash_node *node;
@@ -161,7 +168,7 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
       * create/update or delete the ovsrec mirror(s). */
       SHASH_FOR_EACH (node, &ovn_mirrors) {
          struct ovn_mirror *m = node->data;
-        sync_ovn_mirror(m, ovs_idl_txn, br_int);
+        sync_ovn_mirror(m, ovs_idl_txn, br_int, &ovs_local_mirror_ports);
       }
SHASH_FOR_EACH_SAFE (node, &ovn_mirrors) {
@@ -170,9 +177,52 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
      }
shash_destroy(&ovn_mirrors);
+    shash_destroy(&ovs_local_mirror_ports);
  }
/* Static functions. */
+
+/* Builds mapping from mirror-id to ovsrec_port.
+ */
+static void
+build_ovs_mirror_ports(const struct ovsrec_bridge *br_int,
+                       struct shash *ovs_mirror_ports)
+{
+    int i;
+    for (i = 0; i < br_int->n_ports; i++) {
+        const struct ovsrec_port *port_rec = br_int->ports[i];
+        int j;
+
+        if (!strcmp(port_rec->name, br_int->name)) {
+            continue;
+        }
+
+        for (j = 0; j < port_rec->n_interfaces; j++) {
+            const struct ovsrec_interface *iface_rec;
+
+            iface_rec = port_rec->interfaces[j];
+            const char *mirror_id = smap_get(&iface_rec->external_ids,
+                                             "mirror-id");
+            if (mirror_id) {
+                const struct ovsrec_port *p = shash_find_data(ovs_mirror_ports,
+                                                              mirror_id);
+                if (!p) {
+                    shash_add(ovs_mirror_ports, mirror_id, port_rec);
+                } else {
+                    static struct vlog_rate_limit rl =
+                        VLOG_RATE_LIMIT_INIT(1, 5);
+                    VLOG_WARN_RL(
+                        &rl,
+                        "Invalid configuration: same mirror-id [%s] is "
+                        "configured on interfaces of ports: [%s] and [%s]. "
+                        "Ignoring the configuration on interface [%s]",
+                        mirror_id, port_rec->name, p->name, iface_rec->name);
+                }
+            }
+        }
+    }
+}
+
  static struct ovn_mirror *
  ovn_mirror_create(char *mirror_name)
  {
@@ -266,7 +316,8 @@ check_and_update_interface_table(const struct sbrec_mirror 
*sb_mirror,
static void
  sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn,
-                const struct ovsrec_bridge *br_int)
+                const struct ovsrec_bridge *br_int,
+                struct shash *ovs_mirror_ports)
  {
      if (should_delete_ovs_mirror(m)) {
          /* Delete the ovsrec mirror. */
@@ -281,9 +332,31 @@ sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn 
*ovs_idl_txn,
      }
if (m->sb_mirror && !m->ovs_mirror) {
-        create_ovs_mirror(m, ovs_idl_txn, br_int);
-    } else {
+        create_ovs_mirror(m, ovs_idl_txn, br_int, ovs_mirror_ports);
+        if (!m->ovs_mirror) {
+            return;
+        }
+    } else if (strcmp(m->sb_mirror->type, "local")) {
          check_and_update_interface_table(m->sb_mirror, m->ovs_mirror);
+
+        /* For upgradability, set the "ovn-owned" in case it was not set when
+         * the port was created. */
+        if (!smap_get_bool(&m->ovs_mirror->output_port->external_ids,
+                                   "ovn-owned", false)) {
+            ovsrec_port_update_external_ids_setkey(m->ovs_mirror->output_port,
+                                                   "ovn-owned", "true");
+        }
+    } else {
+        /* type is local, check mirror-id */
+        const struct ovsrec_port *mirror_port =
+            shash_find_data(ovs_mirror_ports, m->sb_mirror->sink);
+        if (!mirror_port) {
+            delete_ovs_mirror(m, br_int);
+            return;
+        }
+        if (mirror_port != m->ovs_mirror->output_port) {
+            ovsrec_mirror_set_output_port(m->ovs_mirror, mirror_port);
+        }
      }
sync_ovs_mirror_ports(m, br_int);
@@ -321,25 +394,39 @@ get_iface_port(const struct ovsrec_interface *iface,
static void
  create_ovs_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn,
-                  const struct ovsrec_bridge *br_int)
+                  const struct ovsrec_bridge *br_int,
+                  struct shash *ovs_mirror_ports)
  {
-    struct ovsrec_interface *iface = ovsrec_interface_insert(ovs_idl_txn);
-    char *port_name = xasprintf("ovn-%s", m->name);
+    const struct ovsrec_port *mirror_port;
+    if (!strcmp(m->sb_mirror->type, "local")) {
+        mirror_port = shash_find_data(ovs_mirror_ports, m->sb_mirror->sink);
+        if (!mirror_port) {
+            return;
+        }
+    } else {
+        struct ovsrec_interface *iface = ovsrec_interface_insert(ovs_idl_txn);
+        char *port_name = xasprintf("ovn-%s", m->name);
- ovsrec_interface_set_name(iface, port_name);
-    ovsrec_interface_set_type(iface, m->sb_mirror->type);
-    set_mirror_iface_options(iface, m->sb_mirror);
+        ovsrec_interface_set_name(iface, port_name);
+        ovsrec_interface_set_type(iface, m->sb_mirror->type);
+        set_mirror_iface_options(iface, m->sb_mirror);
- struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn);
-    ovsrec_port_set_name(port, port_name);
-    ovsrec_port_set_interfaces(port, &iface, 1);
-    ovsrec_bridge_update_ports_addvalue(br_int, port);
+        struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn);
+        ovsrec_port_set_name(port, port_name);
+        ovsrec_port_set_interfaces(port, &iface, 1);
+        ovsrec_bridge_update_ports_addvalue(br_int, port);
- free(port_name);
+        const struct smap port_external_ids =
+            SMAP_CONST1(&port_external_ids, "ovn-owned", "true");
+        ovsrec_port_set_external_ids(port, &port_external_ids);
+
+        free(port_name);
+        mirror_port = port;
+    }
m->ovs_mirror = ovsrec_mirror_insert(ovs_idl_txn);
      ovsrec_mirror_set_name(m->ovs_mirror, m->name);
-    ovsrec_mirror_set_output_port(m->ovs_mirror, port);
+    ovsrec_mirror_set_output_port(m->ovs_mirror, mirror_port);
const struct smap external_ids =
          SMAP_CONST1(&external_ids, "ovn-owned", "true");
@@ -393,8 +480,13 @@ sync_ovs_mirror_ports(struct ovn_mirror *m, const struct 
ovsrec_bridge *br_int)
  static void
  delete_ovs_mirror(struct ovn_mirror *m, const struct ovsrec_bridge *br_int)
  {
-    ovsrec_bridge_update_ports_delvalue(br_int, m->ovs_mirror->output_port);
      ovsrec_bridge_update_mirrors_delvalue(br_int, m->ovs_mirror);
-    ovsrec_port_delete(m->ovs_mirror->output_port);
+    bool ovn_owned = smap_get_bool(&m->ovs_mirror->output_port->external_ids,
+                                   "ovn-owned", false);
+    if (ovn_owned) {
+        ovsrec_bridge_update_ports_delvalue(br_int,
+                                            m->ovs_mirror->output_port);
+        ovsrec_port_delete(m->ovs_mirror->output_port);
+    }
      ovsrec_mirror_delete(m->ovs_mirror);
  }
diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
index 4836a219f93d..dd5632562578 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
  {
      "name": "OVN_Northbound",
-    "version": "7.0.0",
-    "cksum": "94023179 33468",
+    "version": "7.0.1",
+    "cksum": "441625132 33536",
      "tables": {
          "NB_Global": {
              "columns": {
@@ -315,7 +315,8 @@
                  "sink":{"type": "string"},
                  "type": {"type": {"key": {"type": "string",
                                            "enum": ["set", ["gre",
-                                                           "erspan"]]}}},
+                                                           "erspan",
+                                                           "local"]]}}},
                  "index": {"type": "integer"},
                  "external_ids": {
                      "type": {"key": "string", "value": "string",
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 0552eff199a0..20b59df48f4e 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2693,14 +2693,19 @@ or
      <column name="sink">
        <p>
          The value of this field represents the destination/sink of the mirror.
-        The value it takes is an IP address of the sink port.
+        If the <var>type</var> is <code>gre</code> or <code>erspan</code>,
+        the value indicates the tunnel remote IP (either IPv4 or IPv6).
+        For a <var>type</var> of <code>local</code>, this field defines a
+        local interface on the OVS integration bridge to be used as the
+        mirror destination. The interface must possess external-ids:mirror-id
+        that matches this string.
        </p>
      </column>
<column name="type">
        <p>
-        The value of this field represents the type of the tunnel used for
-        sending the mirrored packets.
+        The value of this field specifies the mirror type - <code>gre</code>,
+        <code>erspan</code> or <code>local</code>.
        </p>
      </column>
@@ -2710,6 +2715,7 @@ or
          tunnel type is <code>gre</code>, this field represents the
          <code>GRE</code> key value and if the configured tunnel type is
          <code>erspan</code> it represents the <code>erspan_idx</code> value.
+        It is ignored if the type is <code>local</code>.
        </p>
      </column>
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index 79ba6841e53a..d5ac393f9210 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
  {
      "name": "OVN_Southbound",
-    "version": "20.27.0",
-    "cksum": "4078371916 30328",
+    "version": "20.27.1",
+    "cksum": "1439763681 30400",
      "tables": {
          "SB_Global": {
              "columns": {
@@ -151,8 +151,9 @@
                                                        "to-lport"]]}}},
                  "sink":{"type": "string"},
                  "type": {"type": {"key": {"type": "string",
-                                            "enum": ["set",
-                                                     ["gre", "erspan"]]}}},
+                                          "enum": ["set", ["gre",
+                                                           "erspan",
+                                                           "local"]]}}},
                  "index": {"type": "integer"},
                  "external_ids": {
                      "type": {"key": "string", "value": "string",
diff --git a/ovn-sb.xml b/ovn-sb.xml
index a77f8f4efb38..9599e55f44e5 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -2907,20 +2907,29 @@ tcp.flags = RST;
      <column name="sink">
        <p>
          The value of this field represents the destination/sink of the mirror.
+        If the <var>type</var> is <code>gre</code> or <code>erspan</code>,
+        the value indicates the tunnel remote IP (either IPv4 or IPv6).
+        For a <var>type</var> of <code>local</code>, this field defines a
+        local interface on the OVS integration bridge to be used as the
+        mirror destination. The interface must possess external-ids:mirror-id
+        that matches this string.
        </p>
      </column>
<column name="type">
        <p>
-        The value of this field represents the type of the tunnel used for
-        sending the mirrored packets
+        The value of this field specifies the mirror type - <code>gre</code>,
+        <code>erspan</code> or <code>local</code>.
        </p>
      </column>
<column name="index">
        <p>
-        The value of this field represents the key/idx depending on the
-        tunnel type configured
+        The value of this field represents the tunnel ID. If the configured
+        tunnel type is <code>gre</code>, this field represents the
+        <code>GRE</code> key value and if the configured tunnel type is
+        <code>erspan</code> it represents the <code>erspan_idx</code> value.
+        It is ignored if the type is <code>local</code>.
        </p>
      </column>
diff --git a/tests/ovn.at b/tests/ovn.at
index 213ad18fa50b..4c124e0e58d9 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -16427,7 +16427,7 @@ AT_CLEANUP
  ])
OVN_FOR_EACH_NORTHD([
-AT_SETUP([Mirror])
+AT_SETUP([Mirror - remote])
  AT_KEYWORDS([Mirror])
  ovn_start
@@ -16655,6 +16655,101 @@ OVN_CLEANUP([hv1], [hv2])
  AT_CLEANUP
  ])
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([Mirror - local])
+AT_KEYWORDS([Mirror])
+ovn_start
+
+ovn-nbctl ls-add ls1
+# Create logical port ls1-lp1 in ls1
+ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "00:00:00:01:01:02 10.0.0.2"
+ovn-nbctl lsp-add ls1 ls1-lp2 \
+-- lsp-set-addresses ls1-lp2 "00:00:00:01:01:03 10.0.0.3"
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys -- set bridge br-phys 
other-config:hwaddr=\"00:00:00:01:02:00\"
+ovn_attach n1 br-phys 192.168.1.11
+ovn-appctl -t ovn-controller vlog/set file:dbg
+
+ovs-vsctl -- add-port br-int vif1 -- \
+    set interface vif1 external-ids:iface-id=ls1-lp1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+ovs-vsctl -- add-port br-int vif2 -- \
+    set interface vif2 external-ids:iface-id=ls1-lp2 \
+    options:tx_pcap=hv1/vif2-tx.pcap \
+    options:rxq_pcap=hv1/vif2-rx.pcap \
+    ofport-request=2
+
+# Add two ports as mirroring target
+ovs-vsctl -- add-port br-int mirror1 -- \
+    set interface mirror1 external-ids:mirror-id=sink1 \
+    options:tx_pcap=hv1/mirror1-tx.pcap \
+    options:rxq_pcap=hv1/mirror1-rx.pcap \
+    ofport-request=3
+ovs-vsctl -- add-port br-int mirror2 -- \
+    set interface mirror2 external-ids:mirror-id=sink2 \
+    options:tx_pcap=hv1/mirror2-tx.pcap \
+    options:rxq_pcap=hv1/mirror2-rx.pcap \
+    ofport-request=4
+
+# Create a NB mirror use DB 'create' command.
+uuid1=$(ovn-nbctl create mirror name=mirror-from-lp1 type=local sink=sink1 
filter=from-lport)
+check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror-from-lp1
+
+# Create another NB mirror use 'mirror-add' command.
+check ovn-nbctl mirror-add mirror-to-lp2 local to-lport sink2
+check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror-to-lp2
+
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+AT_CHECK([ovs-vsctl --data=bare --no-heading --columns=name list mirror | grep 
mirror | sort], [0], [dnl
+mirror-from-lp1
+mirror-to-lp2
+])
+
+# Update to wrong mirror-id, the mirror should be deleted.
+check ovn-nbctl set mirror $uuid1 sink=xxx
+check ovn-nbctl --wait=hv sync
+AT_CHECK([ovs-vsctl --data=bare --no-heading --columns=name list mirror | grep 
mirror | sort], [0], [dnl
+mirror-to-lp2
+])
+
+# Change back, and the mirror should be created back
+check ovn-nbctl set mirror $uuid1 sink=sink1
+check ovn-nbctl --wait=hv sync
+AT_CHECK([ovs-vsctl --data=bare --no-heading --columns=name list mirror | grep 
mirror | sort], [0], [dnl
+mirror-from-lp1
+mirror-to-lp2
+])
+
+# Send a packet from lp1 to lp2, should be mirrored to both mirror ports.
+packet="inport==\"ls1-lp1\" && eth.src==00:00:00:01:01:02 && 
eth.dst==00:00:00:01:01:03 &&
+        ip4 && ip.ttl==64 && ip4.src==10.0.0.2 && ip4.dst==10.0.0.3 && icmp"
+AT_CHECK([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
+
+exp_packet="eth.src==00:00:00:01:01:02 && eth.dst==00:00:00:01:01:03 && ip4 &&
+            ip.ttl==64 && ip4.src==10.0.0.2 && ip4.dst==10.0.0.3 && icmp"
+echo $exp_packet | ovstest test-ovn expr-to-packets | sort > expout
+
+OVS_WAIT_UNTIL([
+    rcv_mirror1=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/mirror1-tx.pcap > 
mirror1.packets && cat mirror1.packets | wc -l`
+    rcv_mirror2=`$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/mirror2-tx.pcap > 
mirror2.packets && cat mirror2.packets | wc -l`
+    echo $rcv_mirror1 $rcv_mirror2
+    test $rcv_mirror1 -eq 1 -a $rcv_mirror2 -eq 1])
+
+AT_CHECK([cat mirror1.packets | sort], [0], [expout])
+AT_CHECK([cat mirror2.packets | sort], [0], [expout])
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
+
  OVN_FOR_EACH_NORTHD([
  AT_SETUP([Mirror test bulk updates])
  AT_KEYWORDS([Mirror test bulk updates])
diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
index 54dbdb791e52..c450b9a5ba7d 100644
--- a/utilities/ovn-nbctl.8.xml
+++ b/utilities/ovn-nbctl.8.xml
@@ -1546,7 +1546,7 @@
      <h2> Mirror commands</h2>
      <dl>
        <dt><code>mirror-add</code> <var>m</var> <var>type</var>
-      <var>index</var> <var>filter</var> <var>dest</var></dt>
+      [<var>index</var>] <var>filter</var> <var>dest</var></dt>
        <dd>
          <p>
            Creates a new mirror in the <code>Mirror</code>
@@ -1556,12 +1556,13 @@
<p>
            <var>type</var> specifies the mirror type - <code>gre</code>
-          or <code>erspan</code>.
+          , <code>erspan</code> or <code>local</code>.
          </p>
<p>
            <var>index</var> specifies the tunnel index value (which is
-          an integer).
+          an integer) if the <var>type</var> is <code>gre</code> or
+          <code>erspan</code>.
          </p>
<p>
@@ -1570,7 +1571,12 @@
          </p>
<p>
-          <var>dest</var> specifies the mirror destination IP (v4 or v6).
+          <var>dest</var> specifies the mirror destination IP (v4 or v6)
+          if the <var>type</var> is <code>gre</code> or <code>erspan</code>.
+          For a <var>type</var> of <code>local</code>, this field defines a
+          local interface on the OVS integration bridge to be used as the
+          mirror destination. The interface must possess external-ids:mirror-id
+          that matches this string.
          </p>
        </dd>
diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
index 9399f9462bb1..5f2fbfecfe5b 100644
--- a/utilities/ovn-nbctl.c
+++ b/utilities/ovn-nbctl.c
@@ -273,15 +273,17 @@ QoS commands:\n\
    qos-list SWITCH           print QoS rules for SWITCH\n\
  \n\
  Mirror commands:\n\
-  mirror-add NAME TYPE INDEX FILTER IP\n\
+  mirror-add NAME TYPE [INDEX] FILTER {IP | MIRROR-ID} \n\
                              add a mirror with given name\n\
-                            specify TYPE 'gre' or 'erspan'\n\
+                            specify TYPE 'gre', 'erspan', or 'local'\n\
                              specify the tunnel INDEX value\n\
                                  (indicates key if GRE\n\
                                   erpsan_idx if ERSPAN)\n\
                              specify FILTER for mirroring selection\n\
                                  'to-lport' / 'from-lport'\n\
-                            specify Sink / Destination i.e. Remote IP\n\
+                            specify Sink / Destination i.e. Remote IP, or a\n\
+                                local interface with external-ids:mirror-id\n\
+                                matching MIRROR-ID\n\
    mirror-del [NAME]         remove mirrors\n\
    mirror-list               print mirrors\n\
  \n\
@@ -7406,17 +7408,19 @@ parse_mirror_filter(const char *arg, const char 
**selection_p)
  }
static char * OVS_WARN_UNUSED_RESULT
-parse_mirror_tunnel_type(const char *arg, const char **type_p)
+parse_mirror_type(const char *arg, const char **type_p)
  {
      /* Validate type.  Only require the first letter. */
      if (arg[0] == 'g') {
          *type_p = "gre";
      } else if (arg[0] == 'e') {
          *type_p = "erspan";
+    } else if (arg[0] == 'l') {
+        *type_p = "local";
      } else {
          *type_p = NULL;
-        return xasprintf("%s: type must be \"gre\" or "
-                         "\"erspan\"", arg);
+        return xasprintf("%s: type must be \"gre\", "
+                         "\"erspan\", or \"local\"", arg);
      }
      return NULL;
  }
@@ -7435,16 +7439,16 @@ static void
  nbctl_mirror_add(struct ctl_context *ctx)
  {
      const char *filter = NULL;
-    const char *sink_ip = NULL;
+    const char *sink = NULL;
      const char *type = NULL;
      const char *name = NULL;
-    char *new_sink_ip = NULL;
      int64_t index;
      char *error = NULL;
      const struct nbrec_mirror *mirror_check = NULL;
+    int pos = 1;
/* Mirror Name */
-    name = ctx->argv[1];
+    name = ctx->argv[pos++];
      NBREC_MIRROR_FOR_EACH (mirror_check, ctx->idl) {
          if (!strcmp(mirror_check->name, name)) {
              ctl_error(ctx, "Mirror with %s name already exists.",
@@ -7453,40 +7457,44 @@ nbctl_mirror_add(struct ctl_context *ctx)
          }
      }
- /* Tunnel Type - GRE/ERSPAN */
-    error = parse_mirror_tunnel_type(ctx->argv[2], &type);
+    /* Type - gre/erspan/local */
+    error = parse_mirror_type(ctx->argv[pos++], &type);
      if (error) {
          ctx->error = error;
          return;
      }
- /* tunnel index / GRE key / ERSPAN idx */
-    if (!str_to_long(ctx->argv[3], 10, (long int *) &index)) {
-        ctl_error(ctx, "Invalid Index");
-        return;
+    if (strcmp(type, "local")) {
+        /* tunnel index / GRE key / ERSPAN idx */
+        if (!str_to_long(ctx->argv[pos++], 10, (long int *) &index)) {
+            ctl_error(ctx, "Invalid Index");
+            return;
+        }
      }
/* Filter for mirroring */
-    error = parse_mirror_filter(ctx->argv[4], &filter);
+    error = parse_mirror_filter(ctx->argv[pos++], &filter);
      if (error) {
          ctx->error = error;
          return;
      }
/* Destination / Sink details */
-    sink_ip = ctx->argv[5];
+    sink = ctx->argv[pos++];
- /* check if it is a valid ip */
-    new_sink_ip = normalize_ipv4_addr_str(sink_ip);
-    if (!new_sink_ip) {
-        new_sink_ip = normalize_ipv6_addr_str(sink_ip);
-    }
+    /* check if it is a valid ip unless it is type 'local' */
+    if (strcmp(type, "local")) {
+        char *new_sink_ip = normalize_ipv4_addr_str(sink);
+        if (!new_sink_ip) {
+            new_sink_ip = normalize_ipv6_addr_str(sink);
+        }
- if (!new_sink_ip) {
-        ctl_error(ctx, "Invalid sink ip: %s", sink_ip);
-        return;
+        if (!new_sink_ip) {
+            ctl_error(ctx, "Invalid sink ip: %s", sink);
+            return;
+        }
+        free(new_sink_ip);
      }
-    free(new_sink_ip);
/* Create the mirror. */
      struct nbrec_mirror *mirror = nbrec_mirror_insert(ctx->txn);
@@ -7494,7 +7502,7 @@ nbctl_mirror_add(struct ctl_context *ctx)
      nbrec_mirror_set_index(mirror, index);
      nbrec_mirror_set_filter(mirror, filter);
      nbrec_mirror_set_type(mirror, type);
-    nbrec_mirror_set_sink(mirror, sink_ip);
+    nbrec_mirror_set_sink(mirror, sink);
} @@ -7672,7 +7680,7 @@ static const struct ctl_command_syntax nbctl_commands[] = {
        NULL, "", RO },
/* mirror commands. */
-    { "mirror-add", 5, 5,
+    { "mirror-add", 4, 5,
        "NAME TYPE INDEX FILTER IP",
        nbctl_pre_mirror_add, nbctl_mirror_add, NULL, "--may-exist", RW },
      { "mirror-del", 0, 1, "[NAME]",

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to