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
