On Mon, May 09, 2022 at 03:34:47PM +0530, Abhiram R N wrote:

Hi Ihar,

<just updating the test case link I had provided earlir for reference>

> On Fri, May 06, 2022 at 07:46:17PM -0400, Ihar Hrachyshka wrote:
> > Hi Abhiram,
> > 
> > thanks for sending the patch.
> 
> Thanks for spening time to review it.
> 
> > 
> > This is *not* a comprehensive review. This is an attempt to clarify
> > the scope of the feature, its potential crossover with other pending
> > work, and other high level comments.
> > 
> > API:
> > 1) The proposal more or less directly maps the new OVN mirrors to OVS
> > mirror capabilities. Meaning, it doesn't support any advanced filters
> > to pre-filter traffic mirrored to a sink. Do you think that maybe we
> > will eventually need an ability to filter out some of from/to traffic
> > inside the cluster to optimize for network performance? Ideally, we
> > would support some form of "match" capability like we do for ACLs.
> > AFAIU ovs mirrors don't support such filtering, but maybe it means
> > that they shouldn't be used in OVN mirrors? We could as well program
> > flows to achieve the same effect (plus advanced filters and more).
> > 
> > I may be wrong on the usefulness of "matching" filtering for port
> > mirroring though. I've checked some Tap-as-a-Service definitions and
> > they don't seem to include filtering options beyond to/from port +
> > vlan tags. That said, I don't see why we wouldn't want to allow a user
> > to pre-filter mirrored traffic. Thoughts?
> > 
> 
> The initial goal is exactly what you mentioned at the beginning
> i.e to match the new OVN mirrors to OVS mirror capabilities.
> Currently the focus was only on that.
> Regarding the *match* thing I think its definitely worth keeping
> it in the backlog and do it as future work (or atleast do more
> re-search on how/if it can be useful)
> 
> > 2) This proposal is limited to remote "gre" mirroring. Do you think we
> > should be able to use the same API to mirror to a OVN port inside the
> > cluster? Does the proposed API allow for that? Could e.g. Mirror:type=
> > be set to "internal" and Mirror:sink= to a logical port name? Would
> > there be any issue with such later overload of the mirror API?
> > 
> 
> Just for info, we have added support for "erspan" as well along with "gre".
> 
> Regarding your question to set type "internal" and sink "logical port name"
> I think it is definitely possible to support.
> 
> If I have to think through from implementation point of view
> It might need some changes in nbctl for parameter check.
> Further we have to branch the handling in OVN controller code when we
> get port binding update handler call, based on the "type" either it follows 
> the remote path(in this patch) or local(inside cluster) code path.
> 
> Having said that provinding option now is like marrying the 2 different
> code paths. Because we are trying to re-use existing OVS way of mirroing
> for the remote and for the the local(inside cluster) its a different 
> mechanism. Is it like early to marry the 2 paths now? Rather commit 
> separately and marry them as a future work!
> 
> Open for comments on this.
> 
> > Testing:
> > 1) it's great you included nbctl tests. We will also need tests that
> > would demonstrate that mirrors are doing what is expected: set up
> > mirrors and ports, set up external analyzer / logger, then send some
> > traffic to / from the mirrored port and check that expected packets
> > are delivered through the mirror. Such a test will also help to better
> > understand the intent of the new API.
> 
> Currently I have tested it on my local setup using the ansible script as
> below. But instead of the OVS commands I have used the new OVN ones.
> 
> https://github.com/rh-nfv-int/hw-offload-playbooks/blob/main/testRemotePortMirroring.yml

Above link is not correct. Please refer below link
https://github.com/rh-nfv-int/playbooks/blob/main/regression/ovs-hwol/testRemotePortMirroring.yml

> 
> Originally it used OVS commands:
>             ovs-vsctl add-port br-int gre0 -- set Interface gre0 type=gre
>                               options:local_ip=10.10.10.1 
> options:remote_ip=10.10.10.2
>                                   options:key=0 -- --id=@p get port gre0
>                                   -- --id=@m create mirror name=mirror0 
> output-port=@p 
>                                   -- set bridge br-int mirrors=@m
>             ovs-vsctl -- --id=@p1 get Port {{ physical_interface }}_0 -- add 
> Mirror mirror0 select-dst-port @p1
>             ovs-vsctl -- --id=@p1 get Port {{ physical_interface }}_0 -- add 
> Mirror mirror0 select-src-port @p1
> 
> Now, instead of the above OVS commands below OVN commands I used.
> ovn-nbctl mirror-add mirror0 gre 0 both 10.10.10.2
> ovn-nbctl lsp-attach-mirror sw0-port1 mirror0
> 
> But, I am not very sure on how to fit in such a test inside the OVN.
> Maybe if you can point to some tests or give some guidance there it will help.
> 
> > 
> > Miscellaneous smaller comments:
> > 1) ddlog is no longer supported and should be removed from the tree.
> > We no longer expect the test suite to pass with ddlog, nor we expect
> > ddlog northd implementation changes. You can safely exclude those from
> > next patch respins.
> 
> Sure. Will take care of that . You mean these 3 files I can safely remove
> right? [ovn-nb.dlopts, ovn-sb.dlopts, ovn_northd.dl]
> 
> > 2) Update NEWS file with the new feature.
> 
> Sure. Will update it.
> 
> > 3) I think ovn-nbctl man page needs an update. Perhaps also
> > ovn-architecture man page.
> 
> Sure. Will update it.
> 
> > 
> > Thanks for working on it,
> > Ihar
> Thanks again for taking time to give your valuable comments.
> Regards,
> Abhiram R N
> > 
> > 
> > On Tue, May 3, 2022 at 3:27 PM Abhiram R N <[email protected]> wrote:
> > >
> > > Added changes in ovn-nbctl, ovn-sbctl, northd and in ovn-controller.
> > > While Mirror creation just creates the mirror, the lsp-attach-mirror
> > > triggers the sequence to create Mirror in OVS DB on compute node.
> > > OVS already supports Port Mirroring.
> > >
> > > Note: This is targetted to mirror to destinations anywhere outside the
> > > cluster where the analyser resides and it need not be an OVN node.
> > >
> > > Example commands are as below:
> > >
> > > Mirror creation
> > > ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.2
> > >
> > > Attach a logical port to the mirror.
> > > ovn-nbctl lsp-attach-mirror sw0-port1 mirror1
> > >
> > > Detach a source from Mirror
> > > ovn-nbctl lsp-detach-mirror sw0-port1 mirror1
> > >
> > > Mirror deletion
> > > ovn-nbctl mirror-del mirror1
> > >
> > > Co-authored-by: Veda Barrenkala <[email protected]>
> > > Signed-off-by: Veda Barrenkala <[email protected]>
> > > Signed-off-by: Abhiram R N <[email protected]>
> > > ---
> > >  controller/binding.c        | 275 ++++++++++++++++++++++++++++
> > >  controller/binding.h        |   4 +
> > >  controller/ovn-controller.c |  16 +-
> > >  northd/en-northd.c          |   4 +
> > >  northd/inc-proc-northd.c    |   4 +
> > >  northd/northd.c             | 118 ++++++++++++
> > >  northd/northd.h             |   2 +
> > >  northd/ovn-nb.dlopts        |   1 +
> > >  northd/ovn-sb.dlopts        |   1 +
> > >  northd/ovn_northd.dl        |  10 +
> > >  ovn-nb.ovsschema            |  31 +++-
> > >  ovn-nb.xml                  |  57 ++++++
> > >  ovn-sb.ovsschema            |  23 ++-
> > >  ovn-sb.xml                  |  46 +++++
> > >  tests/ovn-nbctl.at          |  80 ++++++++
> > >  utilities/ovn-nbctl.c       | 356 ++++++++++++++++++++++++++++++++++++
> > >  utilities/ovn-sbctl.c       |   4 +
> > >  17 files changed, 1027 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/controller/binding.c b/controller/binding.c
> > > index 7281b0485..471470cfa 100644
> > > --- a/controller/binding.c
> > > +++ b/controller/binding.c
> > > @@ -64,6 +64,7 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> > >      ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> > >      ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
> > >      ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> > > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors);
> > >
> > >      ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> > >      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> > > @@ -79,6 +80,12 @@ binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> > >
> > >      ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
> > >      ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
> > > +
> > > +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_mirror);
> > > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_name);
> > > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_output_port);
> > > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_dst_port);
> > > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_src_port);
> > >  }
> > >
> > >  static void update_lport_tracking(const struct sbrec_port_binding *pb,
> > > @@ -2226,6 +2233,238 @@ consider_patch_port_for_local_datapaths(const 
> > > struct sbrec_port_binding *pb,
> > >      }
> > >  }
> > >
> > > +static const struct ovsrec_port *
> > > +get_port_for_iface(const struct ovsrec_interface *iface,
> > > +                  const struct ovsrec_bridge *br_int)
> > > +{
> > > +    for (size_t i = 0; i < br_int->n_ports; i++) {
> > > +        const struct ovsrec_port *p = br_int->ports[i];
> > > +        for (size_t j = 0; j < p->n_interfaces; j++) {
> > > +            if (!strcmp(iface->name, p->interfaces[j]->name)) {
> > > +                return p;
> > > +            }
> > > +        }
> > > +    }
> > > +    return NULL;
> > > +}
> > > +
> > > +static void
> > > +mirror_create(const struct sbrec_port_binding *pb,
> > > +              struct binding_ctx_in *b_ctx_in,
> > > +              const struct ovsrec_mirror *mirror)
> > > +{
> > > +    if (pb->up[0] == true) {
> > > +        VLOG_INFO("Mirror rule(s) present for %s ", pb->logical_port);
> > > +        /* Loop through the mirror rules */
> > > +        for (int i =0; i < pb->n_mirror_rules; i++) {
> > > +            /* check if the mirror already exists in OVS DB */
> > > +            bool create_mirror = true;
> > > +            OVSREC_MIRROR_TABLE_FOR_EACH (mirror, 
> > > b_ctx_in->mirror_table) {
> > > +                if (!strcmp(pb->mirror_rules[i]->name, mirror->name)) {
> > > +                      /* Mirror with same name already exists
> > > +                       * No need to create mirror
> > > +                       */
> > > +                      create_mirror = false;
> > > +                      break;
> > > +                }
> > > +            }
> > > +
> > > +            if (create_mirror) {
> > > +
> > > +                struct smap options = SMAP_INITIALIZER(&options);
> > > +                char *port_name, *key;
> > > +
> > > +                key = xasprintf("%ld",(long 
> > > int)pb->mirror_rules[i]->index);
> > > +                smap_add(&options, "remote_ip", 
> > > pb->mirror_rules[i]->sink);
> > > +
> > > +                if (!strcmp(pb->mirror_rules[i]->type, "gre")) {
> > > +                    /* Set the GRE key */
> > > +                    smap_add(&options, "key", key);
> > > +
> > > +                } else if (!strcmp(pb->mirror_rules[i]->type, "erspan")) 
> > > {
> > > +                    /* Set the ERSPAN index */
> > > +                    smap_add(&options, "erspan_idx", key);
> > > +                    smap_add(&options, "erspan_ver","1");
> > > +
> > > +                }
> > > +                struct ovsrec_interface *iface =
> > > +                          ovsrec_interface_insert(b_ctx_in->ovs_idl_txn);
> > > +                port_name = xasprintf("ovn-%s-%s",
> > > +                                       pb->mirror_rules[i]->type,
> > > +                                       pb->mirror_rules[i]->name);
> > > +
> > > +                ovsrec_interface_set_name(iface, port_name);
> > > +                ovsrec_interface_set_type(iface, 
> > > pb->mirror_rules[i]->type);
> > > +                ovsrec_interface_set_options(iface, &options);
> > > +
> > > +                struct ovsrec_port *port =
> > > +                                  
> > > ovsrec_port_insert(b_ctx_in->ovs_idl_txn);
> > > +                ovsrec_port_set_name(port, port_name);
> > > +                ovsrec_port_set_interfaces(port, &iface, 1);
> > > +
> > > +                ovsrec_bridge_update_ports_addvalue(b_ctx_in->br_int, 
> > > port);
> > > +
> > > +                smap_destroy(&options);
> > > +                free(port_name);
> > > +                free(key);
> > > +
> > > +                VLOG_INFO("Creating Mirror in OVS DB");
> > > +                mirror = ovsrec_mirror_insert(b_ctx_in->ovs_idl_txn);
> > > +                ovsrec_mirror_set_name(mirror,pb->mirror_rules[i]->name);
> > > +                ovsrec_mirror_update_output_port_addvalue(mirror, port);
> > > +                ovsrec_bridge_update_mirrors_addvalue(b_ctx_in->br_int,
> > > +                                                                 mirror);
> > > +            }
> > > +
> > > +            const struct ovsrec_interface *iface_rec;
> > > +            const char *iface_id;
> > > +            /* find the interface corresponding to the pb->logical_port 
> > > */
> > > +            OVSREC_INTERFACE_TABLE_FOR_EACH (iface_rec,
> > > +                                             b_ctx_in->iface_table) {
> > > +                iface_id = smap_get(&iface_rec->external_ids, 
> > > "iface-id");
> > > +                if (iface_id) {
> > > +                    if (!strcmp(iface_id, pb->logical_port)) {
> > > +                        VLOG_INFO("Found the interface mapped to 
> > > physical");
> > > +                        break;
> > > +                    }
> > > +                }
> > > +            }
> > > +
> > > +            const struct ovsrec_port *p =
> > > +                              
> > > get_port_for_iface(iface_rec,b_ctx_in->br_int);
> > > +            if (p) {
> > > +                if (!strcmp(pb->mirror_rules[i]->filter,"from-lport")) {
> > > +                    
> > > ovsrec_mirror_update_select_src_port_addvalue(mirror, p);
> > > +                } else if 
> > > (!strcmp(pb->mirror_rules[i]->filter,"to-lport")) {
> > > +                    
> > > ovsrec_mirror_update_select_dst_port_addvalue(mirror, p);
> > > +                } else {
> > > +                    
> > > ovsrec_mirror_update_select_src_port_addvalue(mirror, p);
> > > +                    
> > > ovsrec_mirror_update_select_dst_port_addvalue(mirror, p);
> > > +                }
> > > +            }
> > > +        }
> > > +    }
> > > +}
> > > +
> > > +static void
> > > +mirror_delete(const struct sbrec_port_binding *pb,
> > > +              struct binding_ctx_in *b_ctx_in,
> > > +              struct shash *pb_mirror_map)
> > > +{
> > > +    struct sset pb_mirrors = SSET_INITIALIZER(&pb_mirrors);
> > > +
> > > +    for (size_t i = 0; i < pb->n_mirror_rules ; i++) {
> > > +        sset_add(&pb_mirrors, pb->mirror_rules[i]->name);
> > > +    }
> > > +
> > > +    struct shash_node *mirror_node;
> > > +    SHASH_FOR_EACH (mirror_node, pb_mirror_map) {
> > > +        struct ovsrec_mirror *ovs_mirror = mirror_node->data;
> > > +        if (!sset_find(&pb_mirrors, ovs_mirror->name)) {
> > > +            /* Find if the mirror has other sources i*/
> > > +            if ((ovs_mirror->n_select_dst_port > 1) ||
> > > +                (ovs_mirror->n_select_src_port > 1)) {
> > > +                /* More than 1 source then just
> > > +                 * update the mirror table
> > > +                 */
> > > +                bool done = false;
> > > +                for (size_t i = 0; ((i < ovs_mirror->n_select_dst_port)
> > > +                                                   && (done == false)); 
> > > i++) {
> > > +                    const struct ovsrec_port *port_rec =
> > > +                                               
> > > ovs_mirror->select_dst_port[i];
> > > +                    for (size_t j = 0; j < port_rec->n_interfaces; j++) {
> > > +                        const struct ovsrec_interface *iface_rec;
> > > +
> > > +                        iface_rec = port_rec->interfaces[j];
> > > +                        const char *iface_id =
> > > +                                            
> > > smap_get(&iface_rec->external_ids,
> > > +                                                                  
> > > "iface-id");
> > > +                        if (!strcmp(iface_id,pb->logical_port)) {
> > > +                            
> > > ovsrec_mirror_update_select_dst_port_delvalue(
> > > +                                                        ovs_mirror, 
> > > port_rec);
> > > +                            done = true;
> > > +                            break;
> > > +                        }
> > > +                    }
> > > +                }
> > > +                done = false;
> > > +                for (size_t i = 0; ((i < ovs_mirror->n_select_src_port)
> > > +                                                   && (done == false)); 
> > > i++) {
> > > +                    const struct ovsrec_port *port_rec =
> > > +                                                
> > > ovs_mirror->select_src_port[i];
> > > +                    for (size_t j = 0; j < port_rec->n_interfaces; j++) {
> > > +                        const struct ovsrec_interface *iface_rec;
> > > +
> > > +                        iface_rec = port_rec->interfaces[j];
> > > +                        const char *iface_id =
> > > +                                            
> > > smap_get(&iface_rec->external_ids,
> > > +                                                                  
> > > "iface-id");
> > > +                        if (!strcmp(iface_id,pb->logical_port)) {
> > > +                            
> > > ovsrec_mirror_update_select_src_port_delvalue(
> > > +                                                        ovs_mirror, 
> > > port_rec);
> > > +                            done = true;
> > > +                            break;
> > > +                        }
> > > +                    }
> > > +                }
> > > +            } else {
> > > +                /*
> > > +                 * If only 1 source delete the output port
> > > +                 * and then delete the mirror completely
> > > +                 */
> > > +                VLOG_INFO("Only 1 source for the mirror. Hence delete 
> > > it");
> > > +                ovsrec_bridge_update_ports_delvalue(b_ctx_in->br_int,
> > > +                                                    
> > > ovs_mirror->output_port);
> > > +                ovsrec_bridge_update_mirrors_delvalue(b_ctx_in->br_int,
> > > +                                                            ovs_mirror);
> > > +                ovsrec_port_delete(ovs_mirror->output_port);
> > > +                ovsrec_mirror_delete(ovs_mirror);
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    const char *used_node, *used_next;
> > > +    SSET_FOR_EACH_SAFE (used_node, used_next, &pb_mirrors) {
> > > +        sset_delete(&pb_mirrors, SSET_NODE_FROM_NAME(used_node));
> > > +    }
> > > +    sset_destroy(&pb_mirrors);
> > > +}
> > > +
> > > +static void
> > > +find_port_specific_mirrors (const struct sbrec_port_binding *pb,
> > > +                            struct binding_ctx_in *b_ctx_in,
> > > +                            const struct ovsrec_mirror *mirror,
> > > +                            struct shash *pb_mirror_map)
> > > +{
> > > +    OVSREC_MIRROR_TABLE_FOR_EACH (mirror, b_ctx_in->mirror_table) {
> > > +        for (size_t i = 0; i < mirror->n_select_dst_port; i++) {
> > > +            const struct ovsrec_port *port_rec = 
> > > mirror->select_dst_port[i];
> > > +            for (size_t j = 0; j < port_rec->n_interfaces; j++) {
> > > +                const struct ovsrec_interface *iface_rec;
> > > +                iface_rec = port_rec->interfaces[j];
> > > +                const char *logical_port =
> > > +                    smap_get(&iface_rec->external_ids, "iface-id");
> > > +                if (!strcmp(logical_port, pb->logical_port)) {
> > > +                    shash_add_once(pb_mirror_map, mirror->name, mirror);
> > > +                }
> > > +            }
> > > +        }
> > > +        for (size_t i = 0; i < mirror->n_select_src_port; i++) {
> > > +            const struct ovsrec_port *port_rec = 
> > > mirror->select_src_port[i];
> > > +            for (size_t j = 0; j < port_rec->n_interfaces; j++) {
> > > +                const struct ovsrec_interface *iface_rec;
> > > +                iface_rec = port_rec->interfaces[j];
> > > +                const char *logical_port =
> > > +                    smap_get(&iface_rec->external_ids, "iface-id");
> > > +                if (!strcmp(logical_port, pb->logical_port)) {
> > > +                    shash_add_once(pb_mirror_map, mirror->name, mirror);
> > > +                }
> > > +            }
> > > +        }
> > > +    }
> > > +}
> > > +
> > > +
> > >  /* Returns true if the port binding changes resulted in local binding
> > >   * updates, false otherwise.
> > >   */
> > > @@ -2485,6 +2724,42 @@ delete_done:
> > >      }
> > >
> > >      destroy_qos_map(&qos_map);
> > > +
> > > +    /* Handle Mirror Rule updates */
> > > +    SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb,
> > > +                                               
> > > b_ctx_in->port_binding_table) {
> > > +
> > > +        const struct ovsrec_mirror *mirror = NULL;
> > > +        struct shash port_ovs_mirrors = 
> > > SHASH_INITIALIZER(&port_ovs_mirrors);
> > > +
> > > +        /* Need to find if mirror needs update */
> > > +        find_port_specific_mirrors(pb, b_ctx_in, mirror, 
> > > &port_ovs_mirrors);
> > > +        if (((pb->n_mirror_rules == 0)
> > > +              && (shash_is_empty(&port_ovs_mirrors))) ||
> > > +              (pb->n_mirror_rules == shash_count(&port_ovs_mirrors))) {
> > > +            /* No mirror update */
> > > +        } else {
> > > +            /* Update Mirror */
> > > +            if (pb->n_mirror_rules > shash_count(&port_ovs_mirrors)) {
> > > +                /* create mirror,
> > > +                 * if mirror already exists only update selection
> > > +                 */
> > > +                mirror_create(pb, b_ctx_in, mirror);
> > > +            } else {
> > > +                /* delete mirror,
> > > +                 * if mirror has other sources only update selection
> > > +                 */
> > > +                mirror_delete(pb, b_ctx_in, &port_ovs_mirrors);
> > > +            }
> > > +        }
> > > +        struct shash_node *ovs_mirror_node, *ovs_mirror_next;
> > > +        SHASH_FOR_EACH_SAFE (ovs_mirror_node, ovs_mirror_next,
> > > +                                                  &port_ovs_mirrors) {
> > > +            shash_delete(&port_ovs_mirrors, ovs_mirror_node);
> > > +        }
> > > +        shash_destroy(&port_ovs_mirrors);
> > > +    }
> > > +
> > >      return handled;
> > >  }
> > >
> > > diff --git a/controller/binding.h b/controller/binding.h
> > > index 430a8d9b1..842bb6ab1 100644
> > > --- a/controller/binding.h
> > > +++ b/controller/binding.h
> > > @@ -33,8 +33,10 @@ struct ovsrec_port_table;
> > >  struct ovsrec_qos_table;
> > >  struct ovsrec_bridge_table;
> > >  struct ovsrec_open_vswitch_table;
> > > +struct ovsrec_mirror_table;
> > >  struct sbrec_chassis;
> > >  struct sbrec_port_binding_table;
> > > +struct sbrec_mirror_table;
> > >  struct sset;
> > >  struct sbrec_port_binding;
> > >  struct ds;
> > > @@ -48,7 +50,9 @@ struct binding_ctx_in {
> > >      struct ovsdb_idl_index *sbrec_port_binding_by_name;
> > >      const struct ovsrec_port_table *port_table;
> > >      const struct ovsrec_qos_table *qos_table;
> > > +    const struct ovsrec_mirror_table *mirror_table;
> > >      const struct sbrec_port_binding_table *port_binding_table;
> > > +    const struct sbrec_mirror_table *sb_mirror_table;
> > >      const struct ovsrec_bridge *br_int;
> > >      const struct sbrec_chassis *chassis_rec;
> > >      const struct sset *active_tunnels;
> > > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> > > index 22b7fa935..35f79940a 100644
> > > --- a/controller/ovn-controller.c
> > > +++ b/controller/ovn-controller.c
> > > @@ -977,6 +977,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> > >      SB_NODE(load_balancer, "load_balancer") \
> > >      SB_NODE(fdb, "fdb") \
> > >      SB_NODE(meter, "meter") \
> > > +    SB_NODE(mirror, "mirror") \
> > >      SB_NODE(static_mac_binding, "static_mac_binding")
> > >
> > >  enum sb_engine_node {
> > > @@ -994,7 +995,8 @@ enum sb_engine_node {
> > >      OVS_NODE(bridge, "bridge") \
> > >      OVS_NODE(port, "port") \
> > >      OVS_NODE(interface, "interface") \
> > > -    OVS_NODE(qos, "qos")
> > > +    OVS_NODE(qos, "qos") \
> > > +    OVS_NODE(mirror, "mirror")
> > >
> > >  enum ovs_engine_node {
> > >  #define OVS_NODE(NAME, NAME_STR) OVS_##NAME,
> > > @@ -1222,10 +1224,18 @@ init_binding_ctx(struct engine_node *node,
> > >          (struct ovsrec_qos_table *)EN_OVSDB_GET(
> > >              engine_get_input("OVS_qos", node));
> > >
> > > +    struct ovsrec_mirror_table *mirror_table =
> > > +        (struct ovsrec_mirror_table *)EN_OVSDB_GET(
> > > +            engine_get_input("OVS_mirror", node));
> > > +
> > >      struct sbrec_port_binding_table *pb_table =
> > >          (struct sbrec_port_binding_table *)EN_OVSDB_GET(
> > >              engine_get_input("SB_port_binding", node));
> > >
> > > +    struct sbrec_mirror_table *sb_mirror_table =
> > > +        (struct sbrec_mirror_table *)EN_OVSDB_GET(
> > > +            engine_get_input("SB_mirror", node));
> > > +
> > >      struct ovsdb_idl_index *sbrec_datapath_binding_by_key =
> > >          engine_ovsdb_node_get_index(
> > >                  engine_get_input("SB_datapath_binding", node),
> > > @@ -1251,7 +1261,9 @@ init_binding_ctx(struct engine_node *node,
> > >      b_ctx_in->port_table = port_table;
> > >      b_ctx_in->iface_table = iface_table;
> > >      b_ctx_in->qos_table = qos_table;
> > > +    b_ctx_in->mirror_table = mirror_table;
> > >      b_ctx_in->port_binding_table = pb_table;
> > > +    b_ctx_in->sb_mirror_table = sb_mirror_table;
> > >      b_ctx_in->br_int = br_int;
> > >      b_ctx_in->chassis_rec = chassis;
> > >      b_ctx_in->active_tunnels = &rt_data->active_tunnels;
> > > @@ -3464,8 +3476,10 @@ main(int argc, char *argv[])
> > >      engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL);
> > >      engine_add_input(&en_runtime_data, &en_ovs_bridge, NULL);
> > >      engine_add_input(&en_runtime_data, &en_ovs_qos, NULL);
> > > +    engine_add_input(&en_runtime_data, &en_ovs_mirror, NULL);
> > >
> > >      engine_add_input(&en_runtime_data, &en_sb_chassis, NULL);
> > > +    engine_add_input(&en_runtime_data, &en_sb_mirror, NULL);
> > >      engine_add_input(&en_runtime_data, &en_sb_datapath_binding,
> > >                       runtime_data_sb_datapath_binding_handler);
> > >      engine_add_input(&en_runtime_data, &en_sb_port_binding,
> > > diff --git a/northd/en-northd.c b/northd/en-northd.c
> > > index 4907a1ff2..74cac1b8e 100644
> > > --- a/northd/en-northd.c
> > > +++ b/northd/en-northd.c
> > > @@ -78,6 +78,8 @@ void en_northd_run(struct engine_node *node, void *data)
> > >          EN_OVSDB_GET(engine_get_input("NB_acl", node));
> > >      input_data.nbrec_static_mac_binding_table =
> > >          EN_OVSDB_GET(engine_get_input("NB_static_mac_binding", node));
> > > +    input_data.nbrec_mirror_table =
> > > +        EN_OVSDB_GET(engine_get_input("NB_mirror", node));
> > >
> > >      input_data.sbrec_sb_global_table =
> > >          EN_OVSDB_GET(engine_get_input("SB_sb_global", node));
> > > @@ -111,6 +113,8 @@ void en_northd_run(struct engine_node *node, void 
> > > *data)
> > >          EN_OVSDB_GET(engine_get_input("SB_chassis_private", node));
> > >      input_data.sbrec_static_mac_binding_table =
> > >          EN_OVSDB_GET(engine_get_input("SB_static_mac_binding", node));
> > > +    input_data.sbrec_mirror_table =
> > > +        EN_OVSDB_GET(engine_get_input("SB_mirror", node));
> > >
> > >      northd_run(&input_data, data,
> > >                 eng_ctx->ovnnb_idl_txn,
> > > diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> > > index 43093cb5a..f77daafb8 100644
> > > --- a/northd/inc-proc-northd.c
> > > +++ b/northd/inc-proc-northd.c
> > > @@ -48,6 +48,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd);
> > >      NB_NODE(acl, "acl") \
> > >      NB_NODE(logical_router, "logical_router") \
> > >      NB_NODE(qos, "qos") \
> > > +    NB_NODE(mirror, "mirror") \
> > >      NB_NODE(meter, "meter") \
> > >      NB_NODE(meter_band, "meter_band") \
> > >      NB_NODE(logical_router_port, "logical_router_port") \
> > > @@ -90,6 +91,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd);
> > >      SB_NODE(logical_flow, "logical_flow") \
> > >      SB_NODE(logical_dp_group, "logical_DP_group") \
> > >      SB_NODE(multicast_group, "multicast_group") \
> > > +    SB_NODE(mirror, "mirror") \
> > >      SB_NODE(meter, "meter") \
> > >      SB_NODE(meter_band, "meter_band") \
> > >      SB_NODE(datapath_binding, "datapath_binding") \
> > > @@ -168,6 +170,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> > >      engine_add_input(&en_northd, &en_nb_acl, NULL);
> > >      engine_add_input(&en_northd, &en_nb_logical_router, NULL);
> > >      engine_add_input(&en_northd, &en_nb_qos, NULL);
> > > +    engine_add_input(&en_northd, &en_nb_mirror, NULL);
> > >      engine_add_input(&en_northd, &en_nb_meter, NULL);
> > >      engine_add_input(&en_northd, &en_nb_meter_band, NULL);
> > >      engine_add_input(&en_northd, &en_nb_logical_router_port, NULL);
> > > @@ -190,6 +193,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> > >      engine_add_input(&en_northd, &en_sb_address_set, NULL);
> > >      engine_add_input(&en_northd, &en_sb_port_group, NULL);
> > >      engine_add_input(&en_northd, &en_sb_logical_dp_group, NULL);
> > > +    engine_add_input(&en_northd, &en_sb_mirror, NULL);
> > >      engine_add_input(&en_northd, &en_sb_meter, NULL);
> > >      engine_add_input(&en_northd, &en_sb_meter_band, NULL);
> > >      engine_add_input(&en_northd, &en_sb_datapath_binding, NULL);
> > > diff --git a/northd/northd.c b/northd/northd.c
> > > index a56666297..455458514 100644
> > > --- a/northd/northd.c
> > > +++ b/northd/northd.c
> > > @@ -3204,6 +3204,51 @@ ovn_port_update_sbrec_chassis(
> > >      }
> > >  }
> > >
> > > +static void
> > > +sbrec_port_binding_update_mirror_rules(struct northd_input *input_data,
> > > +                                       const struct ovn_port *op)
> > > +{
> > > +    size_t i = 0;
> > > +    if (op->sb->n_mirror_rules > op->nbsp->n_mirror_rules) {
> > > +        /* Needs deletion in SB */
> > > +        struct shash nb_mirror_rules = 
> > > SHASH_INITIALIZER(&nb_mirror_rules);
> > > +        for (i = 0; i < op->nbsp->n_mirror_rules; i++) {
> > > +            shash_add(&nb_mirror_rules,
> > > +                                 op->nbsp->mirror_rules[i]->name,
> > > +                                 op->nbsp->mirror_rules[i]);
> > > +        }
> > > +
> > > +        for (i = 0; i < op->sb->n_mirror_rules; i++) {
> > > +            if (!shash_find(&nb_mirror_rules,
> > > +                           op->sb->mirror_rules[i]->name)) {
> > > +                    
> > > sbrec_port_binding_update_mirror_rules_delvalue(op->sb,
> > > +                                                 
> > > op->sb->mirror_rules[i]);
> > > +            }
> > > +        }
> > > +
> > > +        struct shash_node *node, *next;
> > > +        SHASH_FOR_EACH_SAFE (node, next, &nb_mirror_rules) {
> > > +            shash_delete(&nb_mirror_rules, node);
> > > +        }
> > > +        shash_destroy(&nb_mirror_rules);
> > > +
> > > +    } else {
> > > +        /* Needs addition in SB */
> > > +        for (i = 0; i < op->nbsp->n_mirror_rules; i++) {
> > > +            const struct sbrec_mirror *sb_mirror;
> > > +            SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror,
> > > +                                         input_data->sbrec_mirror_table) 
> > > {
> > > +                if (!strcmp(sb_mirror->name,
> > > +                            op->nbsp->mirror_rules[i]->name)) {
> > > +                    /* Add the value to SB */
> > > +                    
> > > sbrec_port_binding_update_mirror_rules_addvalue(op->sb,
> > > +                                                                    
> > > sb_mirror);
> > > +                }
> > > +            }
> > > +        }
> > > +    }
> > > +}
> > > +
> > >  static void
> > >  ovn_port_update_sbrec(struct northd_input *input_data,
> > >                        struct ovsdb_idl_txn *ovnsb_txn,
> > > @@ -3548,6 +3593,17 @@ ovn_port_update_sbrec(struct northd_input 
> > > *input_data,
> > >          }
> > >          sbrec_port_binding_set_external_ids(op->sb, &ids);
> > >          smap_destroy(&ids);
> > > +
> > > +        if (!op->nbsp->n_mirror_rules) {
> > > +            /* Nothing is set. Clear mirror_rules from pb. */
> > > +            sbrec_port_binding_set_mirror_rules(op->sb, NULL, 0);
> > > +        } else {
> > > +            /* Check if SB DB update needed */
> > > +            if (op->nbsp->n_mirror_rules != op->sb->n_mirror_rules) {
> > > +                sbrec_port_binding_update_mirror_rules(input_data, op);
> > > +            }
> > > +        }
> > > +
> > >      }
> > >      if (op->tunnel_key != op->sb->tunnel_key) {
> > >          sbrec_port_binding_set_tunnel_key(op->sb, op->tunnel_key);
> > > @@ -14781,6 +14837,67 @@ sync_meters(struct northd_input *input_data,
> > >      shash_destroy(&sb_meters);
> > >  }
> > >
> > > +static void
> > > +sync_mirrors_iterate_nb_mirror(struct ovsdb_idl_txn *ovnsb_txn,
> > > +                             const char *mirror_name,
> > > +                             const struct nbrec_mirror *nb_mirror,
> > > +                             struct shash *sb_mirrors,
> > > +                             struct sset *used_sb_mirrors)
> > > +{
> > > +    const struct sbrec_mirror *sb_mirror;
> > > +    bool new_sb_mirror = false;
> > > +
> > > +    sb_mirror = shash_find_data(sb_mirrors, mirror_name);
> > > +    if (!sb_mirror) {
> > > +        sb_mirror = sbrec_mirror_insert(ovnsb_txn);
> > > +        sbrec_mirror_set_name(sb_mirror, mirror_name);
> > > +        shash_add(sb_mirrors, sb_mirror->name, sb_mirror);
> > > +        new_sb_mirror = true;
> > > +    }
> > > +    sset_add(used_sb_mirrors, mirror_name);
> > > +
> > > +    if (new_sb_mirror) {
> > > +        sbrec_mirror_set_filter(sb_mirror,nb_mirror->filter);
> > > +        sbrec_mirror_set_index(sb_mirror, nb_mirror->index);
> > > +        sbrec_mirror_set_sink(sb_mirror, nb_mirror->sink);
> > > +        sbrec_mirror_set_type(sb_mirror, nb_mirror->type);
> > > +    }
> > > +}
> > > +
> > > +static void
> > > +sync_mirrors(struct northd_input *input_data,
> > > +            struct ovsdb_idl_txn *ovnsb_txn)
> > > +{
> > > +    struct shash sb_mirrors = SHASH_INITIALIZER(&sb_mirrors);
> > > +    struct sset used_sb_mirrors = SSET_INITIALIZER(&used_sb_mirrors);
> > > +
> > > +    const struct sbrec_mirror *sb_mirror;
> > > +    SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, 
> > > input_data->sbrec_mirror_table) {
> > > +        shash_add(&sb_mirrors, sb_mirror->name, sb_mirror);
> > > +    }
> > > +
> > > +    const struct nbrec_mirror *nb_mirror;
> > > +    NBREC_MIRROR_TABLE_FOR_EACH (nb_mirror, 
> > > input_data->nbrec_mirror_table) {
> > > +        sync_mirrors_iterate_nb_mirror(ovnsb_txn, nb_mirror->name, 
> > > nb_mirror,
> > > +                                     &sb_mirrors, &used_sb_mirrors);
> > > +    }
> > > +
> > > +    const char *used_mirror;
> > > +    const char *used_mirror_next;
> > > +    SSET_FOR_EACH_SAFE (used_mirror, used_mirror_next, &used_sb_mirrors) 
> > > {
> > > +        shash_find_and_delete(&sb_mirrors, used_mirror);
> > > +        sset_delete(&used_sb_mirrors, SSET_NODE_FROM_NAME(used_mirror));
> > > +    }
> > > +    sset_destroy(&used_sb_mirrors);
> > > +
> > > +    struct shash_node *node, *next;
> > > +    SHASH_FOR_EACH_SAFE (node, next, &sb_mirrors) {
> > > +        sbrec_mirror_delete(node->data);
> > > +        shash_delete(&sb_mirrors, node);
> > > +    }
> > > +    shash_destroy(&sb_mirrors);
> > > +}
> > > +
> > >  /*
> > >   * struct 'dns_info' is used to sync the DNS records between OVN 
> > > Northbound db
> > >   * and Southbound db.
> > > @@ -15388,6 +15505,7 @@ ovnnb_db_run(struct northd_input *input_data,
> > >      sync_address_sets(input_data, ovnsb_txn, &data->datapaths);
> > >      sync_port_groups(input_data, ovnsb_txn, &data->port_groups);
> > >      sync_meters(input_data, ovnsb_txn, &data->meter_groups);
> > > +    sync_mirrors(input_data, ovnsb_txn);
> > >      sync_dns_entries(input_data, ovnsb_txn, &data->datapaths);
> > >      cleanup_stale_fdb_entries(input_data, &data->datapaths);
> > >      stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec());
> > > diff --git a/northd/northd.h b/northd/northd.h
> > > index 2d804a22e..c30692864 100644
> > > --- a/northd/northd.h
> > > +++ b/northd/northd.h
> > > @@ -30,6 +30,7 @@ struct northd_input {
> > >      const struct nbrec_acl_table *nbrec_acl_table;
> > >      const struct nbrec_static_mac_binding_table
> > >          *nbrec_static_mac_binding_table;
> > > +    const struct nbrec_mirror_table *nbrec_mirror_table;
> > >
> > >      /* Southbound table references */
> > >      const struct sbrec_sb_global_table *sbrec_sb_global_table;
> > > @@ -49,6 +50,7 @@ struct northd_input {
> > >      const struct sbrec_chassis_private_table 
> > > *sbrec_chassis_private_table;
> > >      const struct sbrec_static_mac_binding_table
> > >          *sbrec_static_mac_binding_table;
> > > +    const struct sbrec_mirror_table *sbrec_mirror_table;
> > >
> > >      /* Indexes */
> > >      struct ovsdb_idl_index *sbrec_chassis_by_name;
> > > diff --git a/northd/ovn-nb.dlopts b/northd/ovn-nb.dlopts
> > > index 9a460adef..797ca523f 100644
> > > --- a/northd/ovn-nb.dlopts
> > > +++ b/northd/ovn-nb.dlopts
> > > @@ -20,6 +20,7 @@
> > >  --intern-table Load_Balancer
> > >  --intern-table Logical_Switch
> > >  --intern-table Load_Balancer_Health_Check
> > > +--intern-table Mirror
> > >  --intern-table Meter
> > >  --intern-table NAT
> > >  --intern-table Address_Set
> > > diff --git a/northd/ovn-sb.dlopts b/northd/ovn-sb.dlopts
> > > index 99b65f101..82d614029 100644
> > > --- a/northd/ovn-sb.dlopts
> > > +++ b/northd/ovn-sb.dlopts
> > > @@ -13,6 +13,7 @@
> > >  -o Load_Balancer
> > >  -o Logical_DP_Group
> > >  -o MAC_Binding
> > > +-o Mirror
> > >  -o Meter
> > >  -o Meter_Band
> > >  -o Multicast_Group
> > > diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
> > > index 2fe73959c..22a502fcc 100644
> > > --- a/northd/ovn_northd.dl
> > > +++ b/northd/ovn_northd.dl
> > > @@ -28,6 +28,16 @@ import set
> > >
> > >  index Logical_Flow_Index() on sb::Out_Logical_Flow()
> > >
> > > +/* Mirror table */
> > > +for (mirror in &nb::Mirror) {
> > > +    sb::Out_Mirror(._uuid = mirror._uuid,
> > > +                 .name = mirror.name,
> > > +                 .filter = mirror.filter,
> > > +                 .sink = mirror.sink,
> > > +                 .type = mirror.type,
> > > +                 .index = mirror.index)
> > > +}
> > > +
> > >  /* Meter_Band table */
> > >  for (mb in nb::Meter_Band) {
> > >      sb::Out_Meter_Band(._uuid = mb._uuid,
> > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > > index 174364c8b..01de55222 100644
> > > --- a/ovn-nb.ovsschema
> > > +++ b/ovn-nb.ovsschema
> > > @@ -1,7 +1,7 @@
> > >  {
> > >      "name": "OVN_Northbound",
> > > -    "version": "6.3.0",
> > > -    "cksum": "4042813038 31869",
> > > +    "version": "6.4.0",
> > > +    "cksum": "589874483 33352",
> > >      "tables": {
> > >          "NB_Global": {
> > >              "columns": {
> > > @@ -132,6 +132,11 @@
> > >                                              "refType": "weak"},
> > >                                   "min": 0,
> > >                                   "max": 1}},
> > > +                "mirror_rules": {"type": {"key": {"type": "uuid",
> > > +                                          "refTable": "Mirror",
> > > +                                          "refType": "weak"},
> > > +                                  "min": 0,
> > > +                                  "max": "unlimited"}},
> > >                  "ha_chassis_group": {
> > >                      "type": {"key": {"type": "uuid",
> > >                                       "refTable": "HA_Chassis_Group",
> > > @@ -301,6 +306,28 @@
> > >                      "type": {"key": "string", "value": "string",
> > >                               "min": 0, "max": "unlimited"}}},
> > >              "isRoot": false},
> > > +        "Mirror": {
> > > +            "columns": {
> > > +                "name": {"type": "string"},
> > > +                "filter": {"type": {"key": {"type": "string",
> > > +                                            "enum": ["set", 
> > > ["from-lport",
> > > +                                                             "to-lport",
> > > +                                                             "both"]]}}},
> > > +                "sink":{"type": "string"},
> > > +                "type": {"type": {"key": {"type": "string",
> > > +                                            "enum": ["set", ["gre",
> > > +                                                             
> > > "erspan"]]}}},
> > > +                "index": {"type": "integer"},
> > > +                "src": {"type": {"key": {"type": "uuid",
> > > +                                           "refTable": 
> > > "Logical_Switch_Port",
> > > +                                           "refType": "weak"},
> > > +                                   "min": 0,
> > > +                                   "max": "unlimited"}},
> > > +                "external_ids": {
> > > +                    "type": {"key": "string", "value": "string",
> > > +                             "min": 0, "max": "unlimited"}}},
> > > +            "indexes": [["name"]],
> > > +            "isRoot": true},
> > >          "Meter": {
> > >              "columns": {
> > >                  "name": {"type": "string"},
> > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > > index 9010240a8..c514b7733 100644
> > > --- a/ovn-nb.xml
> > > +++ b/ovn-nb.xml
> > > @@ -1511,6 +1511,11 @@
> > >        </column>
> > >      </group>
> > >
> > > +    <column name="mirror_rules">
> > > +        Mirror rules that apply to logical switch port which is the 
> > > source.
> > > +        Please see the <ref table="Mirror"/> table.
> > > +    </column>
> > > +
> > >      <column name="ha_chassis_group">
> > >        References a row in the OVN Northbound database's
> > >        <ref table="HA_Chassis_Group" db="OVN_Northbound"/> table.
> > > @@ -2432,6 +2437,58 @@
> > >      </column>
> > >    </table>
> > >
> > > +  <table name="Mirror" title="Mirror Entry">
> > > +    <p>
> > > +      Each row in this table represents one Mirror that can be used for
> > > +      port mirroring. These Mirrors are referenced by the
> > > +      <ref column="mirror_rules" table="Logical_Switch_Port"/> column in
> > > +      the <ref table="Logical_Switch_Port"/> table.
> > > +    </p>
> > > +
> > > +    <column name="name">
> > > +      <p>
> > > +        Represents the name of the mirror.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="filter">
> > > +      <p>
> > > +        The value of this field represents selection criteria of the 
> > > mirror.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="sink">
> > > +      <p>
> > > +        The value of this field represents the destination/sink of the 
> > > mirror.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="type">
> > > +      <p>
> > > +        The value of this field represents the type of the tunnel used 
> > > for
> > > +        sending the mirrored packets
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="index">
> > > +      <p>
> > > +        The value of this field represents the key/idx depending on the
> > > +        tunnel type configured
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="src">
> > > +      <p>
> > > +          The value of this field represents the source port for the 
> > > mirror.
> > > +          Please see the <ref table="Logical_Switch_Port"/> table.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="external_ids">
> > > +      See <em>External IDs</em> at the beginning of this document.
> > > +    </column>
> > > +  </table>
> > > +
> > >    <table name="Meter" title="Meter entry">
> > >      <p>
> > >        Each row in this table represents a meter that can be used for QoS 
> > > or
> > > diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
> > > index 66664c840..0493bce7b 100644
> > > --- a/ovn-sb.ovsschema
> > > +++ b/ovn-sb.ovsschema
> > > @@ -1,7 +1,7 @@
> > >  {
> > >      "name": "OVN_Southbound",
> > > -    "version": "20.22.0",
> > > -    "cksum": "1686121686 27471",
> > > +    "version": "20.23.0",
> > > +    "cksum": "654011660 28470",
> > >      "tables": {
> > >          "SB_Global": {
> > >              "columns": {
> > > @@ -142,6 +142,20 @@
> > >              "indexes": [["datapath", "tunnel_key"],
> > >                          ["datapath", "name"]],
> > >              "isRoot": true},
> > > +        "Mirror": {
> > > +            "columns": {
> > > +                "name": {"type": "string"},
> > > +                "filter": {"type": {"key": {"type": "string",
> > > +                                            "enum": ["set",
> > > +                                                     ["from-lport",
> > > +                                                      
> > > "to-lport","both"]]}}},
> > > +                "sink":{"type": "string"},
> > > +                "type": {"type": {"key": {"type": "string",
> > > +                                            "enum": ["set",
> > > +                                                     ["gre", 
> > > "erspan"]]}}},
> > > +                "index": {"type": "integer"}},
> > > +            "indexes": [["name"]],
> > > +            "isRoot": true},
> > >          "Meter": {
> > >              "columns": {
> > >                  "name": {"type": "string"},
> > > @@ -222,6 +236,11 @@
> > >                                              "refTable": "Encap",
> > >                                               "refType": "weak"},
> > >                                      "min": 0, "max": 1}},
> > > +                "mirror_rules": {"type": {"key": {"type": "uuid",
> > > +                                          "refTable": "Mirror",
> > > +                                          "refType": "weak"},
> > > +                                  "min": 0,
> > > +                                  "max": "unlimited"}},
> > >                  "mac": {"type": {"key": "string",
> > >                                   "min": 0,
> > >                                   "max": "unlimited"}},
> > > diff --git a/ovn-sb.xml b/ovn-sb.xml
> > > index 1ffb24e7a..6a81021a4 100644
> > > --- a/ovn-sb.xml
> > > +++ b/ovn-sb.xml
> > > @@ -2632,6 +2632,47 @@ tcp.flags = RST;
> > >      </column>
> > >    </table>
> > >
> > > +  <table name="Mirror" title="Mirror Entry">
> > > +    <p>
> > > +      Each row in this table represents one Mirror that can be used for
> > > +      port mirroring. These Mirrors are referenced by the
> > > +      <ref column="mirror_rules" table="Port_Binding"/> column in
> > > +      the <ref table="Port_Binding"/> table.
> > > +    </p>
> > > +
> > > +    <column name="name">
> > > +      <p>
> > > +        Represents the name of the mirror.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="filter">
> > > +      <p>
> > > +        The value of this field represents selection criteria of the 
> > > mirror.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="sink">
> > > +      <p>
> > > +        The value of this field represents the destination/sink of the 
> > > mirror.
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="type">
> > > +      <p>
> > > +        The value of this field represents the type of the tunnel used 
> > > for
> > > +        sending the mirrored packets
> > > +      </p>
> > > +    </column>
> > > +
> > > +    <column name="index">
> > > +      <p>
> > > +        The value of this field represents the key/idx depending on the
> > > +        tunnel type configured
> > > +      </p>
> > > +    </column>
> > > +  </table>
> > > +
> > >    <table name="Meter" title="Meter entry">
> > >      <p>
> > >        Each row in this table represents a meter that can be used for QoS 
> > > or
> > > @@ -3082,6 +3123,11 @@ tcp.flags = RST;
> > >        </column>
> > >      </group>
> > >
> > > +    <column name="mirror_rules">
> > > +        Mirror rules that apply to the port binding.
> > > +        Please see the <ref table="Mirror"/> table.
> > > +    </column>
> > > +
> > >      <group title="Patch Options">
> > >        <p>
> > >          These options apply to logical ports with <ref column="type"/> of
> > > diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
> > > index f9b9defd0..7643f8884 100644
> > > --- a/tests/ovn-nbctl.at
> > > +++ b/tests/ovn-nbctl.at
> > > @@ -435,6 +435,86 @@ AT_CHECK([ovn-nbctl meter-list], [0], [dnl
> > >
> > >  dnl ---------------------------------------------------------------------
> > >
> > > +OVN_NBCTL_TEST([ovn_nbctl_mirrors], [mirrors], [
> > > +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.1])
> > > +AT_CHECK([ovn-nbctl mirror-add mirror2 erspan 1 both 10.10.10.2])
> > > +AT_CHECK([ovn-nbctl mirror-add mirror3 gre 2 to-lport 10.10.10.3])
> > > +AT_CHECK([ovn-nbctl ls-add sw0])
> > > +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port1])
> > > +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port2])
> > > +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port3])
> > > +
> > > +dnl Add duplicate mirror name
> > > +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.5], 
> > > [1], [], [stderr])
> > > +AT_CHECK([grep 'already exists' stderr], [0], [ignore])
> > > +
> > > +dnl Add mirror with invalid sink port
> > > +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport sw0-port4], [1], 
> > > [], [stderr])
> > > +AT_CHECK([grep 'already exists' stderr], [0], [ignore])
> > > +
> > > +dnl Attach source port to mirror
> > > +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port1 mirror3])
> > > +
> > > +dnl Attach one more source port to mirror
> > > +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port3 mirror3])
> > > +
> > > +dnl Attach invalid source port to mirror
> > > +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port4 mirror3], [1], [], 
> > > [stderr])
> > > +AT_CHECK([grep 'port name not found' stderr], [0], [ignore])
> > > +
> > > +dnl Detach one source port from mirror
> > > +AT_CHECK([ovn-nbctl lsp-detach-mirror sw0-port3 mirror3])
> > > +
> > > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl
> > > +mirror1:
> > > +  Type     :  gre
> > > +  Sink     :  10.10.10.1
> > > +  Filter   :  from-lport
> > > +  Index/Key:  0
> > > +  Sources  :  None attached
> > > +mirror2:
> > > +  Type     :  erspan
> > > +  Sink     :  10.10.10.2
> > > +  Filter   :  both
> > > +  Index/Key:  1
> > > +  Sources  :  None attached
> > > +mirror3:
> > > +  Type     :  gre
> > > +  Sink     :  10.10.10.3
> > > +  Filter   :  to-lport
> > > +  Index/Key:  2
> > > +  Sources  :  sw0-port1
> > > +])
> > > +
> > > +dnl Detach another source port from mirror
> > > +AT_CHECK([ovn-nbctl lsp-detach-mirror sw0-port1 mirror3])
> > > +
> > > +dnl Delete a single mirror.
> > > +AT_CHECK([ovn-nbctl mirror-del mirror3])
> > > +
> > > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl
> > > +mirror1:
> > > +  Type     :  gre
> > > +  Sink     :  10.10.10.1
> > > +  Filter   :  from-lport
> > > +  Index/Key:  0
> > > +  Sources  :  None attached
> > > +mirror2:
> > > +  Type     :  erspan
> > > +  Sink     :  10.10.10.2
> > > +  Filter   :  both
> > > +  Index/Key:  1
> > > +  Sources  :  None attached
> > > +])
> > > +
> > > +dnl Delete all mirrors and remove switch
> > > +AT_CHECK([ovn-nbctl mirror-del])
> > > +AT_CHECK([ovn-nbctl ls-del sw0])
> > > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl
> > > +])])
> > > +
> > > +dnl ---------------------------------------------------------------------
> > > +
> > >  OVN_NBCTL_TEST([ovn_nbctl_nats], [NATs], [
> > >  AT_CHECK([ovn-nbctl lr-add lr0])
> > >  AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [],
> > > diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
> > > index e747f6933..f6ab002ef 100644
> > > --- a/utilities/ovn-nbctl.c
> > > +++ b/utilities/ovn-nbctl.c
> > > @@ -271,6 +271,16 @@ QoS commands:\n\
> > >                              remove QoS rules from SWITCH\n\
> > >    qos-list SWITCH           print QoS rules for SWITCH\n\
> > >  \n\
> > > +Mirror commands:\n\
> > > +  mirror-add NAME TYPE INDEX FILTER IP\n\
> > > +                            add a mirror with given name\n\
> > > +                            specify TYPE 'gre' or 'erspan'\n\
> > > +                            specify INDEX gre key / erpsan idx\n\
> > > +                            specify FILTER for mirroring selection\n\
> > > +                            specify Sink / Destination i.e Remote IP \n\
> > > +  mirror-del [NAME]         remove mirrors\n\
> > > +  mirror-list               print mirrors\n\
> > > +\n\
> > >  Meter commands:\n\
> > >    [--fair]\n\
> > >    meter-add NAME ACTION RATE UNIT [BURST]\n\
> > > @@ -311,6 +321,8 @@ Logical switch port commands:\n\
> > >                              set dhcpv6 options for PORT\n\
> > >    lsp-get-dhcpv6-options PORT  get the dhcpv6 options for PORT\n\
> > >    lsp-get-ls PORT           get the logical switch which the port 
> > > belongs to\n\
> > > +  lsp-attach-mirror PORT MIRROR   attach source PORT to the MIRROR\n\
> > > +  lsp-detach-mirror PORT MIRROR   detach source PORT from the MIRROR\n\
> > >  \n\
> > >  Forwarding group commands:\n\
> > >    [--liveness]\n\
> > > @@ -1685,6 +1697,130 @@ nbctl_pre_lsp_type(struct ctl_context *ctx)
> > >      ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_type);
> > >  }
> > >
> > > +static void
> > > +nbctl_pre_lsp_mirror(struct ctl_context *ctx)
> > > +{
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name);
> > > +    ovsdb_idl_add_column(ctx->idl,
> > > +                         &nbrec_logical_switch_port_col_mirror_rules);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src);
> > > +}
> > > +
> > > +static int
> > > +mirror_cmp(const void *mirror1_, const void *mirror2_)
> > > +{
> > > +    const struct nbrec_mirror *const *mirror_1 = mirror1_;
> > > +    const struct nbrec_mirror *const *mirror_2 = mirror2_;
> > > +
> > > +    const struct nbrec_mirror *mirror1 = *mirror_1;
> > > +    const struct nbrec_mirror *mirror2 = *mirror_2;
> > > +
> > > +    return strcmp(mirror1->name,mirror2->name);
> > > +}
> > > +
> > > +static char * OVS_WARN_UNUSED_RESULT
> > > +mirror_by_name_or_uuid(struct ctl_context *ctx, const char *id,
> > > +                    bool must_exist,
> > > +                    const struct nbrec_mirror **mirror_p)
> > > +{
> > > +    const struct nbrec_mirror *mirror = NULL;
> > > +    *mirror_p = NULL;
> > > +
> > > +    struct uuid mirror_uuid;
> > > +    bool is_uuid = uuid_from_string(&mirror_uuid, id);
> > > +    if (is_uuid) {
> > > +        mirror = nbrec_mirror_get_for_uuid(ctx->idl, &mirror_uuid);
> > > +    }
> > > +
> > > +    if (!mirror) {
> > > +        NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) {
> > > +            if (!strcmp(mirror->name, id)) {
> > > +                break;
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    if (!mirror && must_exist) {
> > > +        return xasprintf("%s: mirror %s not found",
> > > +                         id, is_uuid ? "UUID" : "name");
> > > +    }
> > > +
> > > +    *mirror_p = mirror;
> > > +    return NULL;
> > > +}
> > > +
> > > +static void
> > > +nbctl_lsp_attach_mirror(struct ctl_context *ctx)
> > > +{
> > > +    const char *port = ctx->argv[1];
> > > +    const char *mirror_name = ctx->argv[2];
> > > +    const struct nbrec_logical_switch_port *lsp = NULL;
> > > +    const struct nbrec_mirror *mirror;
> > > +
> > > +    char *error;
> > > +
> > > +    error = lsp_by_name_or_uuid(ctx, port, true, &lsp);
> > > +    if (error) {
> > > +        ctx->error = error;
> > > +        return;
> > > +    }
> > > +
> > > +
> > > +    /*check if a mirror rule actually exists on that name or not*/
> > > +    error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror);
> > > +    if (error) {
> > > +        ctx->error = error;
> > > +        return;
> > > +    }
> > > +
> > > +    /* Check if same mirror rule already exists for the lsp */
> > > +    for (size_t i = 0; i < lsp->n_mirror_rules; i++) {
> > > +        if (!mirror_cmp(&lsp->mirror_rules[i], &mirror)) {
> > > +            bool may_exist = shash_find(&ctx->options, "--may-exist") != 
> > > NULL;
> > > +            if (!may_exist) {
> > > +                ctl_error(ctx, "Same mirror already existed on the lsp 
> > > %s.",
> > > +                          ctx->argv[1]);
> > > +                return;
> > > +            }
> > > +            return;
> > > +        }
> > > +    }
> > > +
> > > +    nbrec_logical_switch_port_update_mirror_rules_addvalue(lsp, mirror);
> > > +    nbrec_mirror_update_src_addvalue(mirror,lsp);
> > > +
> > > +}
> > > +
> > > +static void
> > > +nbctl_lsp_detach_mirror(struct ctl_context *ctx)
> > > +{
> > > +    const char *port = ctx->argv[1];
> > > +    const char *mirror_name = ctx->argv[2];
> > > +    const struct nbrec_logical_switch_port *lsp = NULL;
> > > +    const struct nbrec_mirror *mirror;
> > > +
> > > +    char *error;
> > > +
> > > +    error = lsp_by_name_or_uuid(ctx, port, true, &lsp);
> > > +    if (error) {
> > > +        ctx->error = error;
> > > +        return;
> > > +    }
> > > +
> > > +
> > > +    /*check if a mirror rule actually exists on that name or not*/
> > > +    error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror);
> > > +    if (error) {
> > > +        ctx->error = error;
> > > +        return;
> > > +    }
> > > +
> > > +    nbrec_logical_switch_port_update_mirror_rules_delvalue(lsp, mirror);
> > > +    nbrec_mirror_update_src_delvalue(mirror,lsp);
> > > +
> > > +}
> > > +
> > >  static void
> > >  nbctl_lsp_set_type(struct ctl_context *ctx)
> > >  {
> > > @@ -7161,6 +7297,213 @@ cmd_ha_ch_grp_set_chassis_prio(struct ctl_context 
> > > *ctx)
> > >      nbrec_ha_chassis_set_priority(ha_chassis, priority);
> > >  }
> > >
> > > +static char * OVS_WARN_UNUSED_RESULT
> > > +parse_filter(const char *arg, const char **selection_p)
> > > +{
> > > +    /* Validate selection.  Only require the first letter. */
> > > +    if (arg[0] == 't') {
> > > +        *selection_p = "to-lport";
> > > +    } else if (arg[0] == 'f') {
> > > +        *selection_p = "from-lport";
> > > +    } else if (arg[0] == 'b') {
> > > +        *selection_p = "both";
> > > +    } else {
> > > +        *selection_p = NULL;
> > > +        return xasprintf("%s: selection must be \"to-lport\" or "
> > > +                         "\"from-lport\" or \"both\" ", arg);
> > > +    }
> > > +    return NULL;
> > > +}
> > > +
> > > +static char * OVS_WARN_UNUSED_RESULT
> > > +parse_type(const char *arg, const char **type_p)
> > > +{
> > > +    /* Validate type.  Only require the second letter. */
> > > +    if (arg[0] == 'g') {
> > > +        *type_p = "gre";
> > > +    } else if (arg[0] == 'e') {
> > > +        *type_p = "erspan";
> > > +    } else {
> > > +        *type_p = NULL;
> > > +        return xasprintf("%s: type must be \"gre\" or "
> > > +                         "\"erspan\"", arg);
> > > +    }
> > > +    return NULL;
> > > +}
> > > +
> > > +static void
> > > +nbctl_pre_mirror_add(struct ctl_context *ctx)
> > > +{
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type);
> > > +}
> > > +
> > > +static void
> > > +nbctl_mirror_add(struct ctl_context *ctx)
> > > +{
> > > +    const char *filter = NULL;
> > > +    const char *sink_ip = NULL;
> > > +    const char *type = NULL;
> > > +    const char *name = NULL;
> > > +    char *new_external_ip = NULL;
> > > +    int64_t index;
> > > +    char *error = NULL;
> > > +    const struct nbrec_mirror *mirror_check = NULL;
> > > +
> > > +    /* Mirror Name */
> > > +    name = ctx->argv[1];
> > > +    NBREC_MIRROR_FOR_EACH (mirror_check, ctx->idl) {
> > > +            if (!strcmp(mirror_check->name, name)) {
> > > +                ctl_error(ctx, "Mirror with %s name already exists.",
> > > +                          name);
> > > +                return;
> > > +            }
> > > +        }
> > > +
> > > +    /* Tunnel Type - GRE/ERSPAN */
> > > +    error = parse_type(ctx->argv[2], &type);
> > > +    if (error) {
> > > +        ctx->error = error;
> > > +        return;
> > > +    }
> > > +
> > > +    /* tunnel index / GRE key / ERSPAN idx */
> > > +    index = atoi(ctx->argv[3]);
> > > +
> > > +    /* Filter for mirroring */
> > > +    error = parse_filter(ctx->argv[4], &filter);
> > > +    if (error) {
> > > +        ctx->error = error;
> > > +        return;
> > > +    }
> > > +
> > > +    /* Destination / Sink details */
> > > +    sink_ip = ctx->argv[5];
> > > +
> > > +    /* check if it is a valid ip */
> > > +    new_external_ip = normalize_ipv4_addr_str(sink_ip);
> > > +    if (!new_external_ip) {
> > > +        new_external_ip = normalize_ipv6_addr_str(sink_ip);
> > > +    }
> > > +
> > > +    if (new_external_ip) {
> > > +        free(new_external_ip);
> > > +    } else {
> > > +        ctl_error(ctx, "Invalid sink ip: %s", sink_ip);
> > > +        return;
> > > +    }
> > > +
> > > +    /* Create the mirror. */
> > > +    struct nbrec_mirror *mirror = nbrec_mirror_insert(ctx->txn);
> > > +    nbrec_mirror_set_name(mirror, name);
> > > +    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);
> > > +
> > > +}
> > > +
> > > +static void
> > > +nbctl_pre_mirror_del(struct ctl_context *ctx)
> > > +{
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src);
> > > +}
> > > +
> > > +static void
> > > +nbctl_mirror_del(struct ctl_context *ctx)
> > > +{
> > > +
> > > +    const struct nbrec_mirror *mirror, *next;
> > > +
> > > +    /* If a name is not specified, delete all mirrors. */
> > > +    if (ctx->argc == 1) {
> > > +        NBREC_MIRROR_FOR_EACH_SAFE (mirror, next, ctx->idl) {
> > > +            if (mirror->n_src > 0) {
> > > +                ctl_error(ctx, "Detach mirror source(s) before 
> > > deletion");
> > > +                return;
> > > +            }
> > > +            nbrec_mirror_delete(mirror);
> > > +        }
> > > +        return;
> > > +    }
> > > +
> > > +    /* Remove the matching mirror. */
> > > +    NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) {
> > > +        if (strcmp(ctx->argv[1], mirror->name)) {
> > > +            continue;
> > > +        }
> > > +        if (mirror->n_src > 0) {
> > > +            ctl_error(ctx, "Detach source(s) before mirror %s deletion",
> > > +                      mirror->name);
> > > +            return;
> > > +        }
> > > +        nbrec_mirror_delete(mirror);
> > > +        return;
> > > +    }
> > > +
> > > +}
> > > +
> > > +static void
> > > +nbctl_pre_mirror_list(struct ctl_context *ctx)
> > > +{
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type);
> > > +    ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src);
> > > +}
> > > +
> > > +static void
> > > +nbctl_mirror_list(struct ctl_context *ctx)
> > > +{
> > > +
> > > +    const struct nbrec_mirror **mirrors = NULL;
> > > +    const struct nbrec_mirror *mirror;
> > > +    size_t n_capacity = 0;
> > > +    size_t n_mirrors = 0;
> > > +
> > > +    NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) {
> > > +        if (n_mirrors == n_capacity) {
> > > +            mirrors = x2nrealloc(mirrors, &n_capacity, sizeof *mirrors);
> > > +        }
> > > +
> > > +        mirrors[n_mirrors] = mirror;
> > > +        n_mirrors++;
> > > +    }
> > > +
> > > +    if (n_mirrors) {
> > > +        qsort(mirrors, n_mirrors, sizeof *mirrors, mirror_cmp);
> > > +    }
> > > +
> > > +    for (size_t i = 0; i < n_mirrors; i++) {
> > > +        mirror = mirrors[i];
> > > +        ds_put_format(&ctx->output, "%s:\n", mirror->name);
> > > +        /* print all the values */
> > > +        ds_put_format(&ctx->output, "  Type     :  %s\n", mirror->type);
> > > +        ds_put_format(&ctx->output, "  Sink     :  %s\n", mirror->sink);
> > > +        ds_put_format(&ctx->output, "  Filter   :  %s\n", 
> > > mirror->filter);
> > > +        ds_put_format(&ctx->output, "  Index/Key:  %ld\n",
> > > +                                                 (long 
> > > int)mirror->index);
> > > +        ds_put_cstr(&ctx->output,   "  Sources  :");
> > > +        if (mirror->n_src > 0) {
> > > +            for (size_t j = 0; j < mirror->n_src; j++) {
> > > +                ds_put_format(&ctx->output, "  %s", 
> > > mirror->src[j]->name);
> > > +            }
> > > +        } else {
> > > +            ds_put_cstr(&ctx->output, "  None attached");
> > > +        }
> > > +        ds_put_cstr(&ctx->output, "\n");
> > > +    }
> > > +
> > > +    free(mirrors);
> > > +}
> > > +
> > >  static const struct ctl_table_class tables[NBREC_N_TABLES] = {
> > >      [NBREC_TABLE_DHCP_OPTIONS].row_ids
> > >      = {{&nbrec_logical_switch_port_col_name, NULL,
> > > @@ -7254,6 +7597,15 @@ static const struct ctl_command_syntax 
> > > nbctl_commands[] = {
> > >      { "qos-list", 1, 1, "SWITCH", nbctl_pre_qos_list, nbctl_qos_list,
> > >        NULL, "", RO },
> > >
> > > +    /* mirror commands. */
> > > +    { "mirror-add", 5, 5,
> > > +      "NAME TYPE INDEX FILTER IP",
> > > +      nbctl_pre_mirror_add, nbctl_mirror_add, NULL, "--may-exist", RW },
> > > +    { "mirror-del", 0, 1, "[NAME]",
> > > +      nbctl_pre_mirror_del, nbctl_mirror_del, NULL, "", RW },
> > > +    { "mirror-list", 0, 0, "", nbctl_pre_mirror_list, nbctl_mirror_list,
> > > +      NULL, "", RO },
> > > +
> > >      /* meter commands. */
> > >      { "meter-add", 4, 5, "NAME ACTION RATE UNIT [BURST]", 
> > > nbctl_pre_meter_add,
> > >        nbctl_meter_add, NULL, "--fair,--may-exist", RW },
> > > @@ -7308,6 +7660,10 @@ static const struct ctl_command_syntax 
> > > nbctl_commands[] = {
> > >        nbctl_lsp_get_dhcpv6_options, NULL, "", RO },
> > >      { "lsp-get-ls", 1, 1, "PORT", nbctl_pre_lsp_get_ls, nbctl_lsp_get_ls,
> > >        NULL, "", RO },
> > > +    { "lsp-attach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror,
> > > +      nbctl_lsp_attach_mirror, NULL, "", RW },
> > > +    { "lsp-detach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror,
> > > +      nbctl_lsp_detach_mirror, NULL, "", RW },
> > >
> > >      /* forwarding group commands. */
> > >      { "fwd-group-add", 4, INT_MAX, "SWITCH GROUP VIP VMAC PORT...",
> > > diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
> > > index b008b5d0b..14c24dfb9 100644
> > > --- a/utilities/ovn-sbctl.c
> > > +++ b/utilities/ovn-sbctl.c
> > > @@ -307,6 +307,7 @@ pre_get_info(struct ctl_context *ctx)
> > >      ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis);
> > >      ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_datapath);
> > >      ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_up);
> > > +    ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_mirror_rules);
> > >
> > >      ovsdb_idl_add_column(ctx->idl, 
> > > &sbrec_logical_flow_col_logical_datapath);
> > >      ovsdb_idl_add_column(ctx->idl, 
> > > &sbrec_logical_flow_col_logical_dp_group);
> > > @@ -1424,6 +1425,9 @@ static const struct ctl_table_class 
> > > tables[SBREC_N_TABLES] = {
> > >      [SBREC_TABLE_HA_CHASSIS].row_ids[0]
> > >      = {&sbrec_ha_chassis_col_chassis, NULL, NULL},
> > >
> > > +    [SBREC_TABLE_MIRROR].row_ids[0]
> > > +    = {&sbrec_mirror_col_name, NULL, NULL},
> > > +
> > >      [SBREC_TABLE_METER].row_ids[0]
> > >      = {&sbrec_meter_col_name, NULL, NULL},
> > >
> > > --
> > > 2.27.0
> > >
> > > _______________________________________________
> > > dev mailing list
> > > [email protected]
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
> > 
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to