Message ID | 20221127201407.1579603-3-abhiramrn@gmail.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [ovs-dev,v17,1/3] OVN Remote Port Mirroring: Add new Schemas in NB and SB | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/github-robot-_ovn-kubernetes | success | github build: passed |
ovsrobot/github-robot-_Build_and_Test | fail | github build: failed |
On 11/27/22 15:14, Abhiram R N wrote: > 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. > > Further added test cases in ovn.at to verify end to end > the functioning of Port Mirror and also verify bulk updates > to mirrors. > > Note: This is targeted 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 <vedabarrenkala@gmail.com> > Signed-off-by: Veda Barrenkala <vedabarrenkala@gmail.com> > Signed-off-by: Abhiram R N <abhiramrn@gmail.com> > --- > v16-->v17: Fixed mem leak issue in mirror.c while running > Mirror bulk updates test. > > NEWS | 1 + > controller/automake.mk | 4 +- > controller/mirror.c | 416 +++++++++++++++++++++++++++++ > controller/mirror.h | 33 +++ > controller/ovn-controller.c | 54 ++-- > tests/ovn.at | 514 ++++++++++++++++++++++++++++++++++++ > 6 files changed, 999 insertions(+), 23 deletions(-) > create mode 100644 controller/mirror.c > create mode 100644 controller/mirror.h > > diff --git a/NEWS b/NEWS > index 6c4573b50..dbffcac0f 100644 > --- a/NEWS > +++ b/NEWS > @@ -27,6 +27,7 @@ OVN v22.09.0 - 16 Sep 2022 > any of LR's LRP IP, there is no need to create SNAT entry. Now such > traffic destined to LRP IP is not dropped. > - Bump python version required for building OVN to 3.6. > + - Added Support for Remote Port Mirroring. > > OVN v22.06.0 - 03 Jun 2022 > -------------------------- > diff --git a/controller/automake.mk b/controller/automake.mk > index c2ab1bbe6..334672b4d 100644 > --- a/controller/automake.mk > +++ b/controller/automake.mk > @@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \ > controller/ovsport.h \ > controller/ovsport.c \ > controller/vif-plug.h \ > - controller/vif-plug.c > + controller/vif-plug.c \ > + controller/mirror.h \ > + controller/mirror.c > > controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la > man_MANS += controller/ovn-controller.8 > diff --git a/controller/mirror.c b/controller/mirror.c > new file mode 100644 > index 000000000..ac9d3eea5 > --- /dev/null > +++ b/controller/mirror.c > @@ -0,0 +1,416 @@ > +/* Copyright (c) 2022 Red Hat, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include <config.h> > +#include <unistd.h> > + > +/* library headers */ > +#include "lib/sset.h" > +#include "lib/util.h" > + > +/* OVS includes. */ > +#include "lib/vswitch-idl.h" > +#include "include/openvswitch/shash.h" > +#include "openvswitch/vlog.h" > + > +/* OVN includes. */ > +#include "binding.h" > +#include "lib/ovn-sb-idl.h" > +#include "mirror.h" > + > +VLOG_DEFINE_THIS_MODULE(port_mirror); > + > +struct ovn_mirror { > + char *name; > + const struct sbrec_mirror *sb_mirror; > + const struct ovsrec_mirror *ovs_mirror; > + struct ovs_list mirror_src_lports; > + struct ovs_list mirror_dst_lports; > +}; > + > +struct mirror_lport { > + struct ovs_list list_node; > + > + struct local_binding *lbinding; > +}; > + > +static struct ovn_mirror *ovn_mirror_create(char *mirror_name); > +static void ovn_mirror_add(struct shash *ovn_mirrors, > + struct ovn_mirror *); > +static struct ovn_mirror *ovn_mirror_find(struct shash *ovn_mirrors, > + const char *mirror_name); > +static void ovn_mirror_delete(struct ovn_mirror *); > +static void ovn_mirror_add_lport(struct ovn_mirror *, struct local_binding *); > +static void sync_ovn_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, > + const struct ovsrec_bridge *); > + > +static void create_ovs_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, > + const struct ovsrec_bridge *); > +static void sync_ovs_mirror_ports(struct ovn_mirror *, > + const struct ovsrec_bridge *); > +static void delete_ovs_mirror(struct ovn_mirror *, > + const struct ovsrec_bridge *); > +static bool should_delete_ovs_mirror(struct ovn_mirror *); > + > +static const struct ovsrec_port *get_iface_port( > + const struct ovsrec_interface *, const struct ovsrec_bridge *); > + > + > +void > +mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl) > +{ > + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors); > + > + 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); > + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_external_ids); > +} > + > +void > +mirror_init(void) > +{ > +} > + > +void > +mirror_destroy(void) > +{ > +} > + > +void > +mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, > + const struct ovsrec_mirror_table *ovs_mirror_table, > + const struct sbrec_mirror_table *sb_mirror_table, > + const struct ovsrec_bridge *br_int, > + struct shash *local_bindings) > +{ > + if (!ovs_idl_txn) { > + return; > + } > + > + struct shash ovn_mirrors = SHASH_INITIALIZER(&ovn_mirrors); > + struct shash tmp_mirrors = SHASH_INITIALIZER(&tmp_mirrors); > + > + /* Iterate through sb mirrors and build the 'ovn_mirrors'. */ > + const struct sbrec_mirror *sb_mirror; > + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, sb_mirror_table) { > + struct ovn_mirror *m = ovn_mirror_create(sb_mirror->name); > + m->sb_mirror = sb_mirror; > + ovn_mirror_add(&ovn_mirrors, m); > + } > + > + /* Iterate through ovs mirrors and add to the 'ovn_mirrors'. */ > + const struct ovsrec_mirror *ovs_mirror; > + OVSREC_MIRROR_TABLE_FOR_EACH (ovs_mirror, ovs_mirror_table) { > + bool ovn_owned_mirror = smap_get_bool(&ovs_mirror->external_ids, > + "ovn-owned", false); > + if (!ovn_owned_mirror) { > + continue; > + } > + > + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, ovs_mirror->name); > + if (!m) { > + m = ovn_mirror_create(ovs_mirror->name); > + shash_add(&tmp_mirrors, ovs_mirror->name, m); > + } > + m->ovs_mirror = ovs_mirror; > + } > + > + if (shash_is_empty(&ovn_mirrors)) { > + shash_destroy(&ovn_mirrors); > + if (shash_is_empty(&tmp_mirrors)) { > + shash_destroy(&tmp_mirrors); > + return; > + } else { > + goto cleanup; > + } > + } > + > + /* Iterate through the local bindings and if the local binding's 'pb' has > + * mirrors associated, add it to the ovn_mirror. */ > + struct shash_node *node; > + SHASH_FOR_EACH (node, local_bindings) { > + struct local_binding *lbinding = node->data; > + const struct sbrec_port_binding *pb = > + local_binding_get_primary_pb(local_bindings, lbinding->name); > + if (!pb || !pb->n_mirror_rules) { > + continue; > + } > + > + for (size_t i = 0; i < pb->n_mirror_rules; i++) { > + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, > + pb->mirror_rules[i]->name); > + ovs_assert(m); > + ovn_mirror_add_lport(m, lbinding); > + } > + } > + > + /* Iterate through the built 'ovn_mirrors' and > + * sync with the local ovsdb i.e. > + * create/update or delete the ovsrec mirror(s). */ > + SHASH_FOR_EACH (node, &ovn_mirrors) { > + struct ovn_mirror *m = node->data; > + sync_ovn_mirror(m, ovs_idl_txn, br_int); > + } > + > + SHASH_FOR_EACH_SAFE (node, &ovn_mirrors) { > + ovn_mirror_delete(node->data); > + shash_delete(&ovn_mirrors, node); > + } > + > + shash_destroy(&ovn_mirrors); > + > +cleanup: > + SHASH_FOR_EACH_SAFE (node, &tmp_mirrors) { > + ovn_mirror_delete(node->data); > + shash_delete(&tmp_mirrors, node); > + } > + > + shash_destroy(&tmp_mirrors); > +} > + > +/* Static functions. */ > +static struct ovn_mirror * > +ovn_mirror_create(char *mirror_name) > +{ > + struct ovn_mirror *m = xzalloc(sizeof *m); > + m->name = xstrdup(mirror_name); > + ovs_list_init(&m->mirror_src_lports); > + ovs_list_init(&m->mirror_dst_lports); > + return m; > +} > + > +static void > +ovn_mirror_add(struct shash *ovn_mirrors, struct ovn_mirror *m) > +{ > + shash_add(ovn_mirrors, m->sb_mirror->name, m); > +} > + > +static struct ovn_mirror * > +ovn_mirror_find(struct shash *ovn_mirrors, const char *mirror_name) > +{ > + return shash_find_data(ovn_mirrors, mirror_name); > +} > + > +static void > +ovn_mirror_delete(struct ovn_mirror *m) > +{ > + free(m->name); > + struct mirror_lport *m_lport; > + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_src_lports) { > + free(m_lport); > + } > + > + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_dst_lports) { > + free(m_lport); > + } > + > + free(m); > +} > + > +static void > +ovn_mirror_add_lport(struct ovn_mirror *m, struct local_binding *lbinding) > +{ > + struct mirror_lport *m_lport = xzalloc(sizeof *m_lport); > + m_lport->lbinding = lbinding; > + if (!strcmp(m->sb_mirror->filter, "from-lport")) { > + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); > + } else if (!strcmp(m->sb_mirror->filter, "to-lport")) { > + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); > + } else { > + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); > + m_lport = xzalloc(sizeof *m_lport); > + m_lport->lbinding = lbinding; > + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); > + } > +} > + > +static void > +create_and_set_options(struct ovsrec_interface *iface, > + const struct sbrec_mirror *sb_mirror) > +{ > + struct smap options = SMAP_INITIALIZER(&options); > + char *key; > + > + key = xasprintf("%ld", (long int) sb_mirror->index); > + smap_add(&options, "remote_ip", sb_mirror->sink); > + smap_add(&options, "key", key); > + if (!strcmp(sb_mirror->type, "erspan")) { > + /* Set the ERSPAN index */ > + smap_add(&options, "erspan_idx", key); > + smap_add(&options, "erspan_ver", "1"); > + } > + ovsrec_interface_set_options(iface, &options); > + > + free(key); > + smap_destroy(&options); > +} > + > +static void > +check_and_update_interface_table(const struct sbrec_mirror *sb_mirror, > + const struct ovsrec_mirror *ovs_mirror) > +{ > + char *type; > + struct ovsrec_interface *iface = > + ovs_mirror->output_port->interfaces[0]; > + struct smap *opts = &iface->options; > + const char *erspan_ver = smap_get(opts, "erspan_ver"); > + if (erspan_ver) { > + type = "erspan"; > + } else { > + type = "gre"; > + } > + if (strcmp(type, sb_mirror->type)) { > + ovsrec_interface_set_type(iface, sb_mirror->type); > + } > + create_and_set_options(iface, sb_mirror); > +} > + > +static void > +sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, > + const struct ovsrec_bridge *br_int) > +{ > + if (should_delete_ovs_mirror(m)) { > + /* Delete the ovsrec mirror. */ > + delete_ovs_mirror(m, br_int); > + return; > + } > + > + if (ovs_list_is_empty(&m->mirror_src_lports) && > + ovs_list_is_empty(&m->mirror_dst_lports)) { > + /* Nothing to do. */ > + return; > + } > + > + if (m->sb_mirror && !m->ovs_mirror) { > + create_ovs_mirror(m, ovs_idl_txn, br_int); > + } else { > + check_and_update_interface_table(m->sb_mirror, m->ovs_mirror); > + } > + > + sync_ovs_mirror_ports(m, br_int); > +} > + > +static bool > +should_delete_ovs_mirror(struct ovn_mirror *m) > +{ > + if (!m->ovs_mirror) { > + return false; > + } > + > + if (m->ovs_mirror && !m->sb_mirror) { > + return true; > + } > + > + return (ovs_list_is_empty(&m->mirror_src_lports) && > + ovs_list_is_empty(&m->mirror_dst_lports)); > +} > + > +static const struct ovsrec_port * > +get_iface_port(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 > +create_ovs_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, > + const struct ovsrec_bridge *br_int) > +{ > + struct ovsrec_interface *iface = ovsrec_interface_insert(ovs_idl_txn); > + char *port_name = xasprintf("ovn-%s", m->name); > + > + ovsrec_interface_set_name(iface, port_name); > + ovsrec_interface_set_type(iface, m->sb_mirror->type); > + create_and_set_options(iface, m->sb_mirror); > + > + struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn); > + ovsrec_port_set_name(port, port_name); > + ovsrec_port_set_interfaces(port, &iface, 1); > + ovsrec_bridge_update_ports_addvalue(br_int, port); > + > + free(port_name); > + > + m->ovs_mirror = ovsrec_mirror_insert(ovs_idl_txn); > + ovsrec_mirror_set_name(m->ovs_mirror, m->name); > + ovsrec_mirror_set_output_port(m->ovs_mirror, port); > + > + const struct smap external_ids = > + SMAP_CONST1(&external_ids, "ovn-owned", "true"); > + ovsrec_mirror_set_external_ids(m->ovs_mirror, &external_ids); > + > + ovsrec_bridge_update_mirrors_addvalue(br_int, m->ovs_mirror); > +} > + > +static void > +sync_ovs_mirror_ports(struct ovn_mirror *m, const struct ovsrec_bridge *br_int) > +{ > + struct mirror_lport *m_lport; > + > + if (ovs_list_is_empty(&m->mirror_src_lports)) { > + ovsrec_mirror_set_select_src_port(m->ovs_mirror, NULL, 0); > + } else { > + size_t n_lports = ovs_list_size(&m->mirror_src_lports); > + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * n_lports); > + > + size_t i = 0; > + LIST_FOR_EACH (m_lport, list_node, &m->mirror_src_lports) { > + const struct ovsrec_port *p = > + get_iface_port(m_lport->lbinding->iface, br_int); > + ovs_assert(p); > + ovs_ports[i++] = (struct ovsrec_port *) p; > + } > + > + ovsrec_mirror_set_select_src_port(m->ovs_mirror, ovs_ports, n_lports); > + free(ovs_ports); > + } > + > + if (ovs_list_is_empty(&m->mirror_dst_lports)) { > + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, NULL, 0); > + } else { > + size_t n_lports = ovs_list_size(&m->mirror_dst_lports); > + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * n_lports); > + > + size_t i = 0; > + LIST_FOR_EACH (m_lport, list_node, &m->mirror_dst_lports) { > + const struct ovsrec_port *p = > + get_iface_port(m_lport->lbinding->iface, br_int); > + ovs_assert(p); > + ovs_ports[i++] = (struct ovsrec_port *) p; > + } > + > + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, ovs_ports, n_lports); > + free(ovs_ports); > + } > +} > + > +static void > +delete_ovs_mirror(struct ovn_mirror *m, const struct ovsrec_bridge *br_int) > +{ > + ovsrec_bridge_update_ports_delvalue(br_int, m->ovs_mirror->output_port); > + ovsrec_bridge_update_mirrors_delvalue(br_int, m->ovs_mirror); > + ovsrec_port_delete(m->ovs_mirror->output_port); > + ovsrec_mirror_delete(m->ovs_mirror); > +} > diff --git a/controller/mirror.h b/controller/mirror.h > new file mode 100644 > index 000000000..a79de109d > --- /dev/null > +++ b/controller/mirror.h > @@ -0,0 +1,33 @@ > +/* Copyright (c) 2022 Red Hat, Inc. > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at: > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#ifndef OVN_MIRROR_H > +#define OVN_MIRROR_H 1 > + > +struct ovsdb_idl_txn; > +struct ovsrec_mirror_table; > +struct sbrec_mirror_table; > +struct ovsrec_bridge; > +struct shash; > + > +void mirror_register_ovs_idl(struct ovsdb_idl *); > +void mirror_init(void); > +void mirror_destroy(void); > +void mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, > + const struct ovsrec_mirror_table *, > + const struct sbrec_mirror_table *, > + const struct ovsrec_bridge *, > + struct shash *local_bindings); > +#endif > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index 0752a71ad..0e1d31dbd 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -78,6 +78,7 @@ > #include "lib/inc-proc-eng.h" > #include "lib/ovn-l7.h" > #include "hmapx.h" > +#include "mirror.h" > > VLOG_DEFINE_THIS_MODULE(main); > > @@ -994,6 +995,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name); > ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces); > ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids); > + mirror_register_ovs_idl(ovs_idl); > } > > #define SB_NODES \ > @@ -3619,6 +3621,7 @@ main(int argc, char *argv[]) > patch_init(); > pinctrl_init(); > lflow_init(); > + mirror_init(); > vif_plug_provider_initialize(); > > /* Connect to OVS OVSDB instance. */ > @@ -4195,34 +4198,35 @@ main(int argc, char *argv[]) > > stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, > time_msec()); > - if (ovnsb_idl_txn) { > - if (ofctrl_has_backlog()) { > - /* When there are in-flight messages pending to > - * ovs-vswitchd, we should hold on recomputing so > - * that the previous flow installations won't be > - * delayed. However, we still want to try if > - * recompute is not needed and we can quickly > - * incrementally process the new changes, to avoid > - * unnecessarily forced recomputes later on. This > - * is because the OVSDB change tracker cannot > - * preserve tracked changes across iterations. If > - * change tracking is improved, we can simply skip > - * this round of engine_run and continue processing > - * acculated changes incrementally later when > - * ofctrl_has_backlog() returns false. */ > - engine_run(false); > - } else { > - engine_run(true); > - } > - } else { > - /* Even if there's no SB DB transaction available, > + > + bool allow_engine_recompute = true; > + > + if (!ovnsb_idl_txn || ofctrl_has_backlog()) { > + /* When there are in-flight messages pending to > + * ovs-vswitchd, we should hold on recomputing so > + * that the previous flow installations won't be > + * delayed. However, we still want to try if > + * recompute is not needed and we can quickly > + * incrementally process the new changes, to avoid > + * unnecessarily forced recomputes later on. This > + * is because the OVSDB change tracker cannot > + * preserve tracked changes across iterations. If > + * change tracking is improved, we can simply skip > + * this round of engine_run and continue processing > + * acculated changes incrementally later when > + * ofctrl_has_backlog() returns false. */ > + > + /* Even if there's no SB/OVS DB transaction available, > * try to run the engine so that we can handle any > * incremental changes that don't require a recompute. > * If a recompute is required, the engine will abort, > * triggerring a full run in the next iteration. > */ > - engine_run(false); > + allow_engine_recompute = false; > } > + > + engine_run(allow_engine_recompute); > + The allow_engine_recompute change seems unrelated to mirrors and should probably be removed. It can be added as its own patch at a later time. > stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, > time_msec()); > if (engine_has_updated()) { > @@ -4312,6 +4316,11 @@ main(int argc, char *argv[]) > &runtime_data->local_active_ports_ras); > stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME, > time_msec()); > + mirror_run(ovs_idl_txn, > + ovsrec_mirror_table_get(ovs_idl_loop.idl), > + sbrec_mirror_table_get(ovnsb_idl_loop.idl), > + br_int, > + &runtime_data->lbinding_data.bindings); > /* Updating monitor conditions if runtime data or > * logical datapath goups changed. */ > if (engine_node_changed(&en_runtime_data) > @@ -4555,6 +4564,7 @@ loop_done: > pinctrl_destroy(); > binding_destroy(); > patch_destroy(); > + mirror_destroy(); > if_status_mgr_destroy(if_mgr); > shash_destroy(&vif_plug_deleted_iface_ids); > shash_destroy(&vif_plug_changed_iface_ids); > diff --git a/tests/ovn.at b/tests/ovn.at > index 0ef536509..1769f4906 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -16237,6 +16237,520 @@ OVN_CLEANUP([hv1], [hv2]) > AT_CLEANUP > ]) > > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([Mirror]) > +AT_KEYWORDS([Mirror]) > +ovn_start > + > +# Logical network: > +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > +# and has switch ls2 (172.16.1.0/24) connected to it. > + > +ovn-nbctl lr-add R1 > + > +ovn-nbctl ls-add ls1 > +ovn-nbctl ls-add ls2 > + > +# Connect ls1 to R1 > +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" > + > +# Connect ls2 to R1 > +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" > + > +# Create logical port ls1-lp1 in ls1 > +ovn-nbctl lsp-add ls1 ls1-lp1 \ > +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > + > +# Create logical port ls2-lp1 in ls2 > +ovn-nbctl lsp-add ls2 ls2-lp1 \ > +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > + > +ovn-nbctl lsp-add ls1 ln-public > +ovn-nbctl lsp-set-type ln-public localnet > +ovn-nbctl lsp-set-addresses ln-public unknown > +ovn-nbctl lsp-set-options ln-public network_name=public > + > +# Create one hypervisor and create OVS ports corresponding to logical ports. > +net_add n1 > + > +sim_add hv1 > +as hv1 > +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:01:02:00\" > +ovn_attach n1 br-phys 192.168.1.11 > + > +ovs-vsctl -- add-port br-int vif1 -- \ > + set interface vif1 external-ids:iface-id=ls1-lp1 \ > + options:tx_pcap=hv1/vif1-tx.pcap \ > + options:rxq_pcap=hv1/vif1-rx.pcap \ > + ofport-request=1 > + > +ovs-vsctl -- add-port br-int vif2 -- \ > + set interface vif2 external-ids:iface-id=ls2-lp1 \ > + options:tx_pcap=hv1/vif2-tx.pcap \ > + options:rxq_pcap=hv1/vif2-rx.pcap \ > + ofport-request=1 > + > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > + > +# Allow some time for ovn-northd and ovn-controller to catch up. > +wait_for_ports_up > +check ovn-nbctl --wait=hv sync > +ovn-nbctl dump-flows > sbflows > +AT_CAPTURE_FILE([sbflows]) > + > +for i in 1 2; do > + : > vif$i.expected > +done > + > +net_add n2 > + > +sim_add hv2 > +as hv2 > +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" > +ovn_attach n2 br-phys 192.168.1.12 > + > +OVN_POPULATE_ARP > + > +as hv1 > + > +# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] ENCAP_TYPE FILTER > +# > +# Causes a packet to be received on INPORT. The packet is an ICMPv4 > +# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and > +# ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are > +# provided, then it should be the ip and icmp checksums of the packet > +# responded; otherwise, no reply is expected. > +# In the absence of an ip checksum calculation helpers, this relies > +# on the caller to provide the checksums for the ip and icmp headers. > +# XXX This should be more systematic. > +# > +# INPORT is an lport number, e.g. 11 for vif11. > +# ETH_SRC and ETH_DST are each 12 hex digits. > +# IPV4_SRC and IPV4_DST are each 8 hex digits. > +# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits. > +# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits. > +# ENCAP_TYPE - gre or erspan > +# FILTER - Mirror Filter - to-lport / from-lport > +test_ipv4_icmp_request() { > + local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 ip_chksum=$6 icmp_chksum=$7 > + local exp_ip_chksum=$8 exp_icmp_chksum=$9 mirror_encap_type=${10} mirror_filter=${11} > + shift; shift; shift; shift; shift; shift; shift > + shift; shift; shift; shift; > + > + # Use ttl to exercise section 4.2.2.9 of RFC1812 > + local ip_ttl=02 > + local icmp_id=5fbf > + local icmp_seq=0001 > + local icmp_data=$(seq 1 56 | xargs printf "%02x") > + local icmp_type_code_request=0800 > + local icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} > + local packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload} > + > + as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet > + > + # Expect to receive the reply, if any. In same port where packet was sent. > + # Note: src and dst fields are expected to be reversed. > + local icmp_type_code_response=0000 > + local reply_icmp_ttl=fe > + local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} > + local reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload} > + echo $reply >> vif$inport.expected > + local remote_mac=000000020200 > + local local_mac=000000010200 > + if test ${mirror_encap_type} = "gre" ; then > + local ipv4_gre=4500007e00004000402fb6e9c0a8010bc0a8010c2000655800000000 > + if test ${mirror_filter} = "to-lport" ; then > + local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${reply} > + elif test ${mirror_filter} = "from-lport" ; then > + local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${packet} > + fi > + elif test ${mirror_encap_type} = "erspan" ; then > + local ipv4_erspan=4500008600004000402fb6e1c0a8010bc0a8010c > + local erspan_seq0=100088be000000001000000000000000 > + local erspan_seq1=100088be000000011000000000000000 > + if test ${mirror_filter} = "to-lport" ; then > + local mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq0}${reply} > + elif test ${mirror_filter} = "from-lport" ; then > + local mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq1}${packet} > + fi > + fi > + echo $mirror >> br-phys_n1.expected > + > +} > + > +# Set IPs > +rtr_l2_ip=$(ip_to_hex 172 16 1 1) > +l1_ip=$(ip_to_hex 192 168 1 2) > + > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > + > +# Send ping packet and check for mirrored packet of the reply > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "to-lport" > + > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > + > +as hv1 reset_pcap_file vif1 hv1/vif1 > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > +rm -f br-phys_n1.expected > +rm -f vif1.expected > + > +check ovn-nbctl set mirror . type=erspan > + > +# Send ping packet and check for mirrored packet of the reply > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "to-lport" > + > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > + > +as hv1 reset_pcap_file vif1 hv1/vif1 > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > +rm -f br-phys_n1.expected > +rm -f vif1.expected > + > +check ovn-nbctl set mirror . filter=from-lport > + > +# Send ping packet and check for mirrored packet of the request > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "from-lport" > + > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > + > +as hv1 reset_pcap_file vif1 hv1/vif1 > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > +rm -f br-phys_n1.expected > +rm -f vif1.expected > + > +check ovn-nbctl set mirror . type=gre > + > +# Send ping packet and check for mirrored packet of the request > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "from-lport" > + > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > + > +echo "---------OVN NB Mirror-----" > +ovn-nbctl mirror-list > + > +echo "---------OVS Mirror----" > +ovs-vsctl list Mirror > + > +echo "-----------------------" > + > +echo "Verifying Mirror deletion in OVS" > +# Set vif1 iface-id such that OVN releases port binding > +check ovs-vsctl set interface vif1 external_ids:iface-id=foo > +check ovn-nbctl --wait=hv sync > + > +AT_CHECK([as hv1 ovs-vsctl list Mirror], [0], [dnl > +]) > + > +# Set vif1 iface-id back to ls1-lp1 > +check ovs-vsctl set interface vif1 external_ids:iface-id=ls1-lp1 > +check ovn-nbctl --wait=hv sync > + > +OVS_WAIT_UNTIL([test $(as hv1 ovs-vsctl get Mirror mirror0 name) = "mirror0"]) > + > +# Delete vif1 so that OVN releases port binding > +check ovs-vsctl del-port br-int vif1 > +check ovn-nbctl --wait=hv sync > + > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) > + > +OVN_CLEANUP([hv1], [hv2]) > +AT_CLEANUP > +]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([Mirror test bulk updates]) > +AT_KEYWORDS([Mirror test bulk updates]) > +ovn_start > + > +# Logical network: > +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > +# and has switch ls2 (172.16.1.0/24) connected to it. > + > +ovn-nbctl lr-add R1 > + > +ovn-nbctl ls-add ls1 > +ovn-nbctl ls-add ls2 > + > +# Connect ls1 to R1 > +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" > + > +# Connect ls2 to R1 > +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" > + > +# Create logical port ls1-lp1 in ls1 > +ovn-nbctl lsp-add ls1 ls1-lp1 \ > +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > + > +# Create logical port ls1-lp2 in ls1 > +ovn-nbctl lsp-add ls1 ls1-lp2 \ > +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > + > +# Create logical port ls2-lp1 in ls2 > +ovn-nbctl lsp-add ls2 ls2-lp1 \ > +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > + > +# Create logical port ls2-lp2 in ls2 > +ovn-nbctl lsp-add ls2 ls2-lp2 \ > +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > + > +ovn-nbctl lsp-add ls1 ln-public > +ovn-nbctl lsp-set-type ln-public localnet > +ovn-nbctl lsp-set-addresses ln-public unknown > +ovn-nbctl lsp-set-options ln-public network_name=public > + > +# Create 2 hypervisors and create OVS ports corresponding to logical ports for hv1. > +net_add n1 > + > +sim_add hv1 > +as hv1 > +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:01:02:00\" > +ovn_attach n1 br-phys 192.168.1.11 > + > +net_add n2 > + > +sim_add hv2 > +as hv2 > +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" > +ovn_attach n2 br-phys 192.168.1.12 > + > +OVN_POPULATE_ARP > + > +as hv1 > + > +ovs-vsctl -- add-port br-int vif1 -- \ > + set interface vif1 external-ids:iface-id=ls1-lp1 > + > +ovs-vsctl -- add-port br-int vif2 -- \ > + set interface vif2 external-ids:iface-id=ls2-lp1 > + > +ovs-vsctl -- add-port br-int vif3 -- \ > + set interface vif3 external-ids:iface-id=ls1-lp2 > + > +ovs-vsctl -- add-port br-int vif4 -- \ > + set interface vif4 external-ids:iface-id=ls2-lp2 > + > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > + > +# Allow some time for ovn-northd and ovn-controller to catch up. > +wait_for_ports_up > +check ovn-nbctl --wait=hv sync > +ovn-nbctl dump-flows > sbflows > +AT_CAPTURE_FILE([sbflows]) > + > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > +check ovn-nbctl --wait=hv sync > +origA=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > + > +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > +check ovn-nbctl --wait=hv sync > +origB=$(ovs-vsctl get Mirror mirror1 select_dst_port) > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > + > +check ovn-nbctl --wait=hv sync > + > +as hv1 > +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > + > +check ovn-nbctl mirror-del > +check ovn-nbctl --wait=hv sync > + > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > + > +# Attaches multiple mirrors > + > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > + > +AT_CHECK([test "$orig1" = "$new1"], [0], []) > + > +AT_CHECK([test "$orig2" = "$new2"], [0], []) > + > +# Equal detaches and attaches > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > + > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > + > +# Make sure that ovn-controller has not asserted. > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > + > +AT_CHECK([test "$orig1" = "$new2"], [0], []) > + > +AT_CHECK([test "$orig2" = "$new1"], [0], []) > + > +# Detaches more than attaches > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > +# Make sure that ovn-controller has not asserted. > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > + > +AT_CHECK([test "$origA" = "$new1"], [0], []) > + > +AT_CHECK([test "$origB" = "$new2"], [0], []) > + > +# Attaches more than detaches > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > +# Make sure that ovn-controller has not asserted. > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > + > +AT_CHECK([test "$orig1" = "$new2"], [0], []) > + > +AT_CHECK([test "$orig2" = "$new1"], [0], []) > + > +# Detaches all > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > +check ovn-nbctl --wait=hv sync > + > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) > + > +check ovn-nbctl mirror-add mirror2 gre 2 to-lport 192.168.1.12 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 > +check ovn-nbctl --wait=hv sync > + > +# Attaches SAME port to multiple mirrors > +# and detaches it from existing mirror. > +# More attach than detach > + > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror2 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > + > +AT_CHECK([test "$origA" = "$new1"], [0], []) > +AT_CHECK([test "$origA" = "$new2"], [0], []) > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror2 | wc -l)]) > + > +check ovn-nbctl mirror-add mirror3 gre 3 to-lport 192.168.1.12 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror3 > +check ovn-nbctl --wait=hv sync > + > +# Detaches SAME port from multiple mirrors > +# and attaches it to existing mirror. > +# More detach than attach > + > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror3 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror1 select_dst_port) > +new2=$(ovs-vsctl get Mirror mirror2 select_dst_port) > + > +AT_CHECK([test "$origA" = "$new1"], [0], []) > +AT_CHECK([test "$origA" = "$new2"], [0], []) > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror0 | wc -l)]) > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror3 | wc -l)]) > + > +check ovn-nbctl mirror-del > +check ovn-nbctl --wait=hv sync > +check ovn-nbctl mirror-add mirror0 erspan 0 to-lport 192.168.1.12 > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > +check ovn-nbctl --wait=hv sync > + > +# Make sure different fields of mirror resource set from OVN > +# propagates to OVS correctly > + > +check as hv1 ovn-appctl -t ovn-controller debug/pause > +check ovn-nbctl set mirror . filter=from-lport > +check ovn-nbctl set mirror . type=gre > +check ovn-nbctl set mirror . index=123 > +check as hv1 ovn-appctl -t ovn-controller debug/resume > + > +check ovn-nbctl --wait=hv sync > + > +as hv1 > +new1=$(ovs-vsctl get Mirror mirror0 select_src_port) > +AT_CHECK([test "$origA" = "$new1"], [0], []) > +type=$(ovs-vsctl get Interface ovn-mirror0 type) > +OVS_WAIT_UNTIL([test "gre" = "$type"]) > +index=$(ovs-vsctl get Interface ovn-mirror0 options:key | grep 123 | wc -l) > +OVS_WAIT_UNTIL([test "1" = "$index"]) > + > +OVN_CLEANUP([hv1],[hv2]) > +AT_CLEANUP > +]) > > OVN_FOR_EACH_NORTHD([ > AT_SETUP([Port Groups])
Hi Mark, Thanks for your review. Agreed. I will remove the 'allow_engine_recompute' related changes in the next patch set. Thanks & Regards, Abhiram R N On Tue, Nov 29, 2022 at 3:24 AM Mark Michelson <mmichels@redhat.com> wrote: > On 11/27/22 15:14, Abhiram R N wrote: > > 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. > > > > Further added test cases in ovn.at to verify end to end > > the functioning of Port Mirror and also verify bulk updates > > to mirrors. > > > > Note: This is targeted 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 <vedabarrenkala@gmail.com> > > Signed-off-by: Veda Barrenkala <vedabarrenkala@gmail.com> > > Signed-off-by: Abhiram R N <abhiramrn@gmail.com> > > --- > > v16-->v17: Fixed mem leak issue in mirror.c while running > > Mirror bulk updates test. > > > > NEWS | 1 + > > controller/automake.mk | 4 +- > > controller/mirror.c | 416 +++++++++++++++++++++++++++++ > > controller/mirror.h | 33 +++ > > controller/ovn-controller.c | 54 ++-- > > tests/ovn.at | 514 ++++++++++++++++++++++++++++++++++++ > > 6 files changed, 999 insertions(+), 23 deletions(-) > > create mode 100644 controller/mirror.c > > create mode 100644 controller/mirror.h > > > > diff --git a/NEWS b/NEWS > > index 6c4573b50..dbffcac0f 100644 > > --- a/NEWS > > +++ b/NEWS > > @@ -27,6 +27,7 @@ OVN v22.09.0 - 16 Sep 2022 > > any of LR's LRP IP, there is no need to create SNAT entry. Now > such > > traffic destined to LRP IP is not dropped. > > - Bump python version required for building OVN to 3.6. > > + - Added Support for Remote Port Mirroring. > > > > OVN v22.06.0 - 03 Jun 2022 > > -------------------------- > > diff --git a/controller/automake.mk b/controller/automake.mk > > index c2ab1bbe6..334672b4d 100644 > > --- a/controller/automake.mk > > +++ b/controller/automake.mk > > @@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \ > > controller/ovsport.h \ > > controller/ovsport.c \ > > controller/vif-plug.h \ > > - controller/vif-plug.c > > + controller/vif-plug.c \ > > + controller/mirror.h \ > > + controller/mirror.c > > > > controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/ > libopenvswitch.la > > man_MANS += controller/ovn-controller.8 > > diff --git a/controller/mirror.c b/controller/mirror.c > > new file mode 100644 > > index 000000000..ac9d3eea5 > > --- /dev/null > > +++ b/controller/mirror.c > > @@ -0,0 +1,416 @@ > > +/* Copyright (c) 2022 Red Hat, Inc. > > + * > > + * Licensed under the Apache License, Version 2.0 (the "License"); > > + * you may not use this file except in compliance with the License. > > + * You may obtain a copy of the License at: > > + * > > + * http://www.apache.org/licenses/LICENSE-2.0 > > + * > > + * Unless required by applicable law or agreed to in writing, software > > + * distributed under the License is distributed on an "AS IS" BASIS, > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > implied. > > + * See the License for the specific language governing permissions and > > + * limitations under the License. > > + */ > > + > > +#include <config.h> > > +#include <unistd.h> > > + > > +/* library headers */ > > +#include "lib/sset.h" > > +#include "lib/util.h" > > + > > +/* OVS includes. */ > > +#include "lib/vswitch-idl.h" > > +#include "include/openvswitch/shash.h" > > +#include "openvswitch/vlog.h" > > + > > +/* OVN includes. */ > > +#include "binding.h" > > +#include "lib/ovn-sb-idl.h" > > +#include "mirror.h" > > + > > +VLOG_DEFINE_THIS_MODULE(port_mirror); > > + > > +struct ovn_mirror { > > + char *name; > > + const struct sbrec_mirror *sb_mirror; > > + const struct ovsrec_mirror *ovs_mirror; > > + struct ovs_list mirror_src_lports; > > + struct ovs_list mirror_dst_lports; > > +}; > > + > > +struct mirror_lport { > > + struct ovs_list list_node; > > + > > + struct local_binding *lbinding; > > +}; > > + > > +static struct ovn_mirror *ovn_mirror_create(char *mirror_name); > > +static void ovn_mirror_add(struct shash *ovn_mirrors, > > + struct ovn_mirror *); > > +static struct ovn_mirror *ovn_mirror_find(struct shash *ovn_mirrors, > > + const char *mirror_name); > > +static void ovn_mirror_delete(struct ovn_mirror *); > > +static void ovn_mirror_add_lport(struct ovn_mirror *, struct > local_binding *); > > +static void sync_ovn_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, > > + const struct ovsrec_bridge *); > > + > > +static void create_ovs_mirror(struct ovn_mirror *, struct ovsdb_idl_txn > *, > > + const struct ovsrec_bridge *); > > +static void sync_ovs_mirror_ports(struct ovn_mirror *, > > + const struct ovsrec_bridge *); > > +static void delete_ovs_mirror(struct ovn_mirror *, > > + const struct ovsrec_bridge *); > > +static bool should_delete_ovs_mirror(struct ovn_mirror *); > > + > > +static const struct ovsrec_port *get_iface_port( > > + const struct ovsrec_interface *, const struct ovsrec_bridge *); > > + > > + > > +void > > +mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl) > > +{ > > + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors); > > + > > + 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); > > + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_external_ids); > > +} > > + > > +void > > +mirror_init(void) > > +{ > > +} > > + > > +void > > +mirror_destroy(void) > > +{ > > +} > > + > > +void > > +mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, > > + const struct ovsrec_mirror_table *ovs_mirror_table, > > + const struct sbrec_mirror_table *sb_mirror_table, > > + const struct ovsrec_bridge *br_int, > > + struct shash *local_bindings) > > +{ > > + if (!ovs_idl_txn) { > > + return; > > + } > > + > > + struct shash ovn_mirrors = SHASH_INITIALIZER(&ovn_mirrors); > > + struct shash tmp_mirrors = SHASH_INITIALIZER(&tmp_mirrors); > > + > > + /* Iterate through sb mirrors and build the 'ovn_mirrors'. */ > > + const struct sbrec_mirror *sb_mirror; > > + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, sb_mirror_table) { > > + struct ovn_mirror *m = ovn_mirror_create(sb_mirror->name); > > + m->sb_mirror = sb_mirror; > > + ovn_mirror_add(&ovn_mirrors, m); > > + } > > + > > + /* Iterate through ovs mirrors and add to the 'ovn_mirrors'. */ > > + const struct ovsrec_mirror *ovs_mirror; > > + OVSREC_MIRROR_TABLE_FOR_EACH (ovs_mirror, ovs_mirror_table) { > > + bool ovn_owned_mirror = smap_get_bool(&ovs_mirror->external_ids, > > + "ovn-owned", false); > > + if (!ovn_owned_mirror) { > > + continue; > > + } > > + > > + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, > ovs_mirror->name); > > + if (!m) { > > + m = ovn_mirror_create(ovs_mirror->name); > > + shash_add(&tmp_mirrors, ovs_mirror->name, m); > > + } > > + m->ovs_mirror = ovs_mirror; > > + } > > + > > + if (shash_is_empty(&ovn_mirrors)) { > > + shash_destroy(&ovn_mirrors); > > + if (shash_is_empty(&tmp_mirrors)) { > > + shash_destroy(&tmp_mirrors); > > + return; > > + } else { > > + goto cleanup; > > + } > > + } > > + > > + /* Iterate through the local bindings and if the local binding's > 'pb' has > > + * mirrors associated, add it to the ovn_mirror. */ > > + struct shash_node *node; > > + SHASH_FOR_EACH (node, local_bindings) { > > + struct local_binding *lbinding = node->data; > > + const struct sbrec_port_binding *pb = > > + local_binding_get_primary_pb(local_bindings, > lbinding->name); > > + if (!pb || !pb->n_mirror_rules) { > > + continue; > > + } > > + > > + for (size_t i = 0; i < pb->n_mirror_rules; i++) { > > + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, > > + > pb->mirror_rules[i]->name); > > + ovs_assert(m); > > + ovn_mirror_add_lport(m, lbinding); > > + } > > + } > > + > > + /* Iterate through the built 'ovn_mirrors' and > > + * sync with the local ovsdb i.e. > > + * create/update or delete the ovsrec mirror(s). */ > > + SHASH_FOR_EACH (node, &ovn_mirrors) { > > + struct ovn_mirror *m = node->data; > > + sync_ovn_mirror(m, ovs_idl_txn, br_int); > > + } > > + > > + SHASH_FOR_EACH_SAFE (node, &ovn_mirrors) { > > + ovn_mirror_delete(node->data); > > + shash_delete(&ovn_mirrors, node); > > + } > > + > > + shash_destroy(&ovn_mirrors); > > + > > +cleanup: > > + SHASH_FOR_EACH_SAFE (node, &tmp_mirrors) { > > + ovn_mirror_delete(node->data); > > + shash_delete(&tmp_mirrors, node); > > + } > > + > > + shash_destroy(&tmp_mirrors); > > +} > > + > > +/* Static functions. */ > > +static struct ovn_mirror * > > +ovn_mirror_create(char *mirror_name) > > +{ > > + struct ovn_mirror *m = xzalloc(sizeof *m); > > + m->name = xstrdup(mirror_name); > > + ovs_list_init(&m->mirror_src_lports); > > + ovs_list_init(&m->mirror_dst_lports); > > + return m; > > +} > > + > > +static void > > +ovn_mirror_add(struct shash *ovn_mirrors, struct ovn_mirror *m) > > +{ > > + shash_add(ovn_mirrors, m->sb_mirror->name, m); > > +} > > + > > +static struct ovn_mirror * > > +ovn_mirror_find(struct shash *ovn_mirrors, const char *mirror_name) > > +{ > > + return shash_find_data(ovn_mirrors, mirror_name); > > +} > > + > > +static void > > +ovn_mirror_delete(struct ovn_mirror *m) > > +{ > > + free(m->name); > > + struct mirror_lport *m_lport; > > + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_src_lports) { > > + free(m_lport); > > + } > > + > > + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_dst_lports) { > > + free(m_lport); > > + } > > + > > + free(m); > > +} > > + > > +static void > > +ovn_mirror_add_lport(struct ovn_mirror *m, struct local_binding > *lbinding) > > +{ > > + struct mirror_lport *m_lport = xzalloc(sizeof *m_lport); > > + m_lport->lbinding = lbinding; > > + if (!strcmp(m->sb_mirror->filter, "from-lport")) { > > + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); > > + } else if (!strcmp(m->sb_mirror->filter, "to-lport")) { > > + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); > > + } else { > > + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); > > + m_lport = xzalloc(sizeof *m_lport); > > + m_lport->lbinding = lbinding; > > + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); > > + } > > +} > > + > > +static void > > +create_and_set_options(struct ovsrec_interface *iface, > > + const struct sbrec_mirror *sb_mirror) > > +{ > > + struct smap options = SMAP_INITIALIZER(&options); > > + char *key; > > + > > + key = xasprintf("%ld", (long int) sb_mirror->index); > > + smap_add(&options, "remote_ip", sb_mirror->sink); > > + smap_add(&options, "key", key); > > + if (!strcmp(sb_mirror->type, "erspan")) { > > + /* Set the ERSPAN index */ > > + smap_add(&options, "erspan_idx", key); > > + smap_add(&options, "erspan_ver", "1"); > > + } > > + ovsrec_interface_set_options(iface, &options); > > + > > + free(key); > > + smap_destroy(&options); > > +} > > + > > +static void > > +check_and_update_interface_table(const struct sbrec_mirror *sb_mirror, > > + const struct ovsrec_mirror *ovs_mirror) > > +{ > > + char *type; > > + struct ovsrec_interface *iface = > > + ovs_mirror->output_port->interfaces[0]; > > + struct smap *opts = &iface->options; > > + const char *erspan_ver = smap_get(opts, "erspan_ver"); > > + if (erspan_ver) { > > + type = "erspan"; > > + } else { > > + type = "gre"; > > + } > > + if (strcmp(type, sb_mirror->type)) { > > + ovsrec_interface_set_type(iface, sb_mirror->type); > > + } > > + create_and_set_options(iface, sb_mirror); > > +} > > + > > +static void > > +sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, > > + const struct ovsrec_bridge *br_int) > > +{ > > + if (should_delete_ovs_mirror(m)) { > > + /* Delete the ovsrec mirror. */ > > + delete_ovs_mirror(m, br_int); > > + return; > > + } > > + > > + if (ovs_list_is_empty(&m->mirror_src_lports) && > > + ovs_list_is_empty(&m->mirror_dst_lports)) { > > + /* Nothing to do. */ > > + return; > > + } > > + > > + if (m->sb_mirror && !m->ovs_mirror) { > > + create_ovs_mirror(m, ovs_idl_txn, br_int); > > + } else { > > + check_and_update_interface_table(m->sb_mirror, m->ovs_mirror); > > + } > > + > > + sync_ovs_mirror_ports(m, br_int); > > +} > > + > > +static bool > > +should_delete_ovs_mirror(struct ovn_mirror *m) > > +{ > > + if (!m->ovs_mirror) { > > + return false; > > + } > > + > > + if (m->ovs_mirror && !m->sb_mirror) { > > + return true; > > + } > > + > > + return (ovs_list_is_empty(&m->mirror_src_lports) && > > + ovs_list_is_empty(&m->mirror_dst_lports)); > > +} > > + > > +static const struct ovsrec_port * > > +get_iface_port(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 > > +create_ovs_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn > *ovs_idl_txn, > > + const struct ovsrec_bridge *br_int) > > +{ > > + struct ovsrec_interface *iface = > ovsrec_interface_insert(ovs_idl_txn); > > + char *port_name = xasprintf("ovn-%s", m->name); > > + > > + ovsrec_interface_set_name(iface, port_name); > > + ovsrec_interface_set_type(iface, m->sb_mirror->type); > > + create_and_set_options(iface, m->sb_mirror); > > + > > + struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn); > > + ovsrec_port_set_name(port, port_name); > > + ovsrec_port_set_interfaces(port, &iface, 1); > > + ovsrec_bridge_update_ports_addvalue(br_int, port); > > + > > + free(port_name); > > + > > + m->ovs_mirror = ovsrec_mirror_insert(ovs_idl_txn); > > + ovsrec_mirror_set_name(m->ovs_mirror, m->name); > > + ovsrec_mirror_set_output_port(m->ovs_mirror, port); > > + > > + const struct smap external_ids = > > + SMAP_CONST1(&external_ids, "ovn-owned", "true"); > > + ovsrec_mirror_set_external_ids(m->ovs_mirror, &external_ids); > > + > > + ovsrec_bridge_update_mirrors_addvalue(br_int, m->ovs_mirror); > > +} > > + > > +static void > > +sync_ovs_mirror_ports(struct ovn_mirror *m, const struct ovsrec_bridge > *br_int) > > +{ > > + struct mirror_lport *m_lport; > > + > > + if (ovs_list_is_empty(&m->mirror_src_lports)) { > > + ovsrec_mirror_set_select_src_port(m->ovs_mirror, NULL, 0); > > + } else { > > + size_t n_lports = ovs_list_size(&m->mirror_src_lports); > > + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * > n_lports); > > + > > + size_t i = 0; > > + LIST_FOR_EACH (m_lport, list_node, &m->mirror_src_lports) { > > + const struct ovsrec_port *p = > > + get_iface_port(m_lport->lbinding->iface, br_int); > > + ovs_assert(p); > > + ovs_ports[i++] = (struct ovsrec_port *) p; > > + } > > + > > + ovsrec_mirror_set_select_src_port(m->ovs_mirror, ovs_ports, > n_lports); > > + free(ovs_ports); > > + } > > + > > + if (ovs_list_is_empty(&m->mirror_dst_lports)) { > > + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, NULL, 0); > > + } else { > > + size_t n_lports = ovs_list_size(&m->mirror_dst_lports); > > + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * > n_lports); > > + > > + size_t i = 0; > > + LIST_FOR_EACH (m_lport, list_node, &m->mirror_dst_lports) { > > + const struct ovsrec_port *p = > > + get_iface_port(m_lport->lbinding->iface, br_int); > > + ovs_assert(p); > > + ovs_ports[i++] = (struct ovsrec_port *) p; > > + } > > + > > + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, ovs_ports, > n_lports); > > + free(ovs_ports); > > + } > > +} > > + > > +static void > > +delete_ovs_mirror(struct ovn_mirror *m, const struct ovsrec_bridge > *br_int) > > +{ > > + ovsrec_bridge_update_ports_delvalue(br_int, > m->ovs_mirror->output_port); > > + ovsrec_bridge_update_mirrors_delvalue(br_int, m->ovs_mirror); > > + ovsrec_port_delete(m->ovs_mirror->output_port); > > + ovsrec_mirror_delete(m->ovs_mirror); > > +} > > diff --git a/controller/mirror.h b/controller/mirror.h > > new file mode 100644 > > index 000000000..a79de109d > > --- /dev/null > > +++ b/controller/mirror.h > > @@ -0,0 +1,33 @@ > > +/* Copyright (c) 2022 Red Hat, Inc. > > + * > > + * Licensed under the Apache License, Version 2.0 (the "License"); > > + * you may not use this file except in compliance with the License. > > + * You may obtain a copy of the License at: > > + * > > + * http://www.apache.org/licenses/LICENSE-2.0 > > + * > > + * Unless required by applicable law or agreed to in writing, software > > + * distributed under the License is distributed on an "AS IS" BASIS, > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > implied. > > + * See the License for the specific language governing permissions and > > + * limitations under the License. > > + */ > > + > > +#ifndef OVN_MIRROR_H > > +#define OVN_MIRROR_H 1 > > + > > +struct ovsdb_idl_txn; > > +struct ovsrec_mirror_table; > > +struct sbrec_mirror_table; > > +struct ovsrec_bridge; > > +struct shash; > > + > > +void mirror_register_ovs_idl(struct ovsdb_idl *); > > +void mirror_init(void); > > +void mirror_destroy(void); > > +void mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, > > + const struct ovsrec_mirror_table *, > > + const struct sbrec_mirror_table *, > > + const struct ovsrec_bridge *, > > + struct shash *local_bindings); > > +#endif > > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > > index 0752a71ad..0e1d31dbd 100644 > > --- a/controller/ovn-controller.c > > +++ b/controller/ovn-controller.c > > @@ -78,6 +78,7 @@ > > #include "lib/inc-proc-eng.h" > > #include "lib/ovn-l7.h" > > #include "hmapx.h" > > +#include "mirror.h" > > > > VLOG_DEFINE_THIS_MODULE(main); > > > > @@ -994,6 +995,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > > ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name); > > ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces); > > ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids); > > + mirror_register_ovs_idl(ovs_idl); > > } > > > > #define SB_NODES \ > > @@ -3619,6 +3621,7 @@ main(int argc, char *argv[]) > > patch_init(); > > pinctrl_init(); > > lflow_init(); > > + mirror_init(); > > vif_plug_provider_initialize(); > > > > /* Connect to OVS OVSDB instance. */ > > @@ -4195,34 +4198,35 @@ main(int argc, char *argv[]) > > > > stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, > > time_msec()); > > - if (ovnsb_idl_txn) { > > - if (ofctrl_has_backlog()) { > > - /* When there are in-flight messages > pending to > > - * ovs-vswitchd, we should hold on > recomputing so > > - * that the previous flow installations > won't be > > - * delayed. However, we still want to try > if > > - * recompute is not needed and we can > quickly > > - * incrementally process the new changes, > to avoid > > - * unnecessarily forced recomputes later > on. This > > - * is because the OVSDB change tracker > cannot > > - * preserve tracked changes across > iterations. If > > - * change tracking is improved, we can > simply skip > > - * this round of engine_run and continue > processing > > - * acculated changes incrementally later > when > > - * ofctrl_has_backlog() returns false. */ > > - engine_run(false); > > - } else { > > - engine_run(true); > > - } > > - } else { > > - /* Even if there's no SB DB transaction > available, > > + > > + bool allow_engine_recompute = true; > > + > > + if (!ovnsb_idl_txn || ofctrl_has_backlog()) { > > + /* When there are in-flight messages pending to > > + * ovs-vswitchd, we should hold on recomputing > so > > + * that the previous flow installations won't be > > + * delayed. However, we still want to try if > > + * recompute is not needed and we can quickly > > + * incrementally process the new changes, to > avoid > > + * unnecessarily forced recomputes later on. > This > > + * is because the OVSDB change tracker cannot > > + * preserve tracked changes across iterations. > If > > + * change tracking is improved, we can simply > skip > > + * this round of engine_run and continue > processing > > + * acculated changes incrementally later when > > + * ofctrl_has_backlog() returns false. */ > > + > > + /* Even if there's no SB/OVS DB transaction > available, > > * try to run the engine so that we can handle > any > > * incremental changes that don't require a > recompute. > > * If a recompute is required, the engine will > abort, > > * triggerring a full run in the next > iteration. > > */ > > - engine_run(false); > > + allow_engine_recompute = false; > > } > > + > > + engine_run(allow_engine_recompute); > > + > > The allow_engine_recompute change seems unrelated to mirrors and should > probably be removed. It can be added as its own patch at a later time. > > > stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, > > time_msec()); > > if (engine_has_updated()) { > > @@ -4312,6 +4316,11 @@ main(int argc, char *argv[]) > > > &runtime_data->local_active_ports_ras); > > stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME, > > time_msec()); > > + mirror_run(ovs_idl_txn, > > + > ovsrec_mirror_table_get(ovs_idl_loop.idl), > > + > sbrec_mirror_table_get(ovnsb_idl_loop.idl), > > + br_int, > > + > &runtime_data->lbinding_data.bindings); > > /* Updating monitor conditions if runtime data > or > > * logical datapath goups changed. */ > > if (engine_node_changed(&en_runtime_data) > > @@ -4555,6 +4564,7 @@ loop_done: > > pinctrl_destroy(); > > binding_destroy(); > > patch_destroy(); > > + mirror_destroy(); > > if_status_mgr_destroy(if_mgr); > > shash_destroy(&vif_plug_deleted_iface_ids); > > shash_destroy(&vif_plug_changed_iface_ids); > > diff --git a/tests/ovn.at b/tests/ovn.at > > index 0ef536509..1769f4906 100644 > > --- a/tests/ovn.at > > +++ b/tests/ovn.at > > @@ -16237,6 +16237,520 @@ OVN_CLEANUP([hv1], [hv2]) > > AT_CLEANUP > > ]) > > > > +OVN_FOR_EACH_NORTHD([ > > +AT_SETUP([Mirror]) > > +AT_KEYWORDS([Mirror]) > > +ovn_start > > + > > +# Logical network: > > +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > > +# and has switch ls2 (172.16.1.0/24) connected to it. > > + > > +ovn-nbctl lr-add R1 > > + > > +ovn-nbctl ls-add ls1 > > +ovn-nbctl ls-add ls2 > > + > > +# Connect ls1 to R1 > > +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > > +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > > + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" > > + > > +# Connect ls2 to R1 > > +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > > +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > > + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" > > + > > +# Create logical port ls1-lp1 in ls1 > > +ovn-nbctl lsp-add ls1 ls1-lp1 \ > > +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > > + > > +# Create logical port ls2-lp1 in ls2 > > +ovn-nbctl lsp-add ls2 ls2-lp1 \ > > +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > > + > > +ovn-nbctl lsp-add ls1 ln-public > > +ovn-nbctl lsp-set-type ln-public localnet > > +ovn-nbctl lsp-set-addresses ln-public unknown > > +ovn-nbctl lsp-set-options ln-public network_name=public > > + > > +# Create one hypervisor and create OVS ports corresponding to logical > ports. > > +net_add n1 > > + > > +sim_add hv1 > > +as hv1 > > +ovs-vsctl add-br br-phys -- set bridge br-phys > other-config:hwaddr=\"00:00:00:01:02:00\" > > +ovn_attach n1 br-phys 192.168.1.11 > > + > > +ovs-vsctl -- add-port br-int vif1 -- \ > > + set interface vif1 external-ids:iface-id=ls1-lp1 \ > > + options:tx_pcap=hv1/vif1-tx.pcap \ > > + options:rxq_pcap=hv1/vif1-rx.pcap \ > > + ofport-request=1 > > + > > +ovs-vsctl -- add-port br-int vif2 -- \ > > + set interface vif2 external-ids:iface-id=ls2-lp1 \ > > + options:tx_pcap=hv1/vif2-tx.pcap \ > > + options:rxq_pcap=hv1/vif2-rx.pcap \ > > + ofport-request=1 > > + > > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > > + > > +# Allow some time for ovn-northd and ovn-controller to catch up. > > +wait_for_ports_up > > +check ovn-nbctl --wait=hv sync > > +ovn-nbctl dump-flows > sbflows > > +AT_CAPTURE_FILE([sbflows]) > > + > > +for i in 1 2; do > > + : > vif$i.expected > > +done > > + > > +net_add n2 > > + > > +sim_add hv2 > > +as hv2 > > +ovs-vsctl add-br br-phys -- set bridge br-phys > other-config:hwaddr=\"00:00:00:02:02:00\" > > +ovn_attach n2 br-phys 192.168.1.12 > > + > > +OVN_POPULATE_ARP > > + > > +as hv1 > > + > > +# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST > IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] ENCAP_TYPE FILTER > > +# > > +# Causes a packet to be received on INPORT. The packet is an ICMPv4 > > +# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and > > +# ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are > > +# provided, then it should be the ip and icmp checksums of the packet > > +# responded; otherwise, no reply is expected. > > +# In the absence of an ip checksum calculation helpers, this relies > > +# on the caller to provide the checksums for the ip and icmp headers. > > +# XXX This should be more systematic. > > +# > > +# INPORT is an lport number, e.g. 11 for vif11. > > +# ETH_SRC and ETH_DST are each 12 hex digits. > > +# IPV4_SRC and IPV4_DST are each 8 hex digits. > > +# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits. > > +# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits. > > +# ENCAP_TYPE - gre or erspan > > +# FILTER - Mirror Filter - to-lport / from-lport > > +test_ipv4_icmp_request() { > > + local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 > ip_chksum=$6 icmp_chksum=$7 > > + local exp_ip_chksum=$8 exp_icmp_chksum=$9 mirror_encap_type=${10} > mirror_filter=${11} > > + shift; shift; shift; shift; shift; shift; shift > > + shift; shift; shift; shift; > > + > > + # Use ttl to exercise section 4.2.2.9 of RFC1812 > > + local ip_ttl=02 > > + local icmp_id=5fbf > > + local icmp_seq=0001 > > + local icmp_data=$(seq 1 56 | xargs printf "%02x") > > + local icmp_type_code_request=0800 > > + local > icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} > > + local > packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload} > > + > > + as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet > > + > > + # Expect to receive the reply, if any. In same port where packet > was sent. > > + # Note: src and dst fields are expected to be reversed. > > + local icmp_type_code_response=0000 > > + local reply_icmp_ttl=fe > > + local > reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} > > + local > reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload} > > + echo $reply >> vif$inport.expected > > + local remote_mac=000000020200 > > + local local_mac=000000010200 > > + if test ${mirror_encap_type} = "gre" ; then > > + local > ipv4_gre=4500007e00004000402fb6e9c0a8010bc0a8010c2000655800000000 > > + if test ${mirror_filter} = "to-lport" ; then > > + local > mirror=${remote_mac}${local_mac}0800${ipv4_gre}${reply} > > + elif test ${mirror_filter} = "from-lport" ; then > > + local > mirror=${remote_mac}${local_mac}0800${ipv4_gre}${packet} > > + fi > > + elif test ${mirror_encap_type} = "erspan" ; then > > + local ipv4_erspan=4500008600004000402fb6e1c0a8010bc0a8010c > > + local erspan_seq0=100088be000000001000000000000000 > > + local erspan_seq1=100088be000000011000000000000000 > > + if test ${mirror_filter} = "to-lport" ; then > > + local > mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq0}${reply} > > + elif test ${mirror_filter} = "from-lport" ; then > > + local > mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq1}${packet} > > + fi > > + fi > > + echo $mirror >> br-phys_n1.expected > > + > > +} > > + > > +# Set IPs > > +rtr_l2_ip=$(ip_to_hex 172 16 1 1) > > +l1_ip=$(ip_to_hex 192 168 1 2) > > + > > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > > + > > +# Send ping packet and check for mirrored packet of the reply > > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip > 0000 8510 03ff 8d10 "gre" "to-lport" > > + > > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > [br-phys_n1.expected]) > > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > > + > > +as hv1 reset_pcap_file vif1 hv1/vif1 > > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > > +rm -f br-phys_n1.expected > > +rm -f vif1.expected > > + > > +check ovn-nbctl set mirror . type=erspan > > + > > +# Send ping packet and check for mirrored packet of the reply > > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip > 0000 8510 03ff 8d10 "erspan" "to-lport" > > + > > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > [br-phys_n1.expected]) > > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > > + > > +as hv1 reset_pcap_file vif1 hv1/vif1 > > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > > +rm -f br-phys_n1.expected > > +rm -f vif1.expected > > + > > +check ovn-nbctl set mirror . filter=from-lport > > + > > +# Send ping packet and check for mirrored packet of the request > > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip > 0000 8510 03ff 8d10 "erspan" "from-lport" > > + > > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > [br-phys_n1.expected]) > > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > > + > > +as hv1 reset_pcap_file vif1 hv1/vif1 > > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > > +rm -f br-phys_n1.expected > > +rm -f vif1.expected > > + > > +check ovn-nbctl set mirror . type=gre > > + > > +# Send ping packet and check for mirrored packet of the request > > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip > 0000 8510 03ff 8d10 "gre" "from-lport" > > + > > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > [br-phys_n1.expected]) > > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > > + > > +echo "---------OVN NB Mirror-----" > > +ovn-nbctl mirror-list > > + > > +echo "---------OVS Mirror----" > > +ovs-vsctl list Mirror > > + > > +echo "-----------------------" > > + > > +echo "Verifying Mirror deletion in OVS" > > +# Set vif1 iface-id such that OVN releases port binding > > +check ovs-vsctl set interface vif1 external_ids:iface-id=foo > > +check ovn-nbctl --wait=hv sync > > + > > +AT_CHECK([as hv1 ovs-vsctl list Mirror], [0], [dnl > > +]) > > + > > +# Set vif1 iface-id back to ls1-lp1 > > +check ovs-vsctl set interface vif1 external_ids:iface-id=ls1-lp1 > > +check ovn-nbctl --wait=hv sync > > + > > +OVS_WAIT_UNTIL([test $(as hv1 ovs-vsctl get Mirror mirror0 name) = > "mirror0"]) > > + > > +# Delete vif1 so that OVN releases port binding > > +check ovs-vsctl del-port br-int vif1 > > +check ovn-nbctl --wait=hv sync > > + > > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) > > + > > +OVN_CLEANUP([hv1], [hv2]) > > +AT_CLEANUP > > +]) > > + > > +OVN_FOR_EACH_NORTHD([ > > +AT_SETUP([Mirror test bulk updates]) > > +AT_KEYWORDS([Mirror test bulk updates]) > > +ovn_start > > + > > +# Logical network: > > +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > > +# and has switch ls2 (172.16.1.0/24) connected to it. > > + > > +ovn-nbctl lr-add R1 > > + > > +ovn-nbctl ls-add ls1 > > +ovn-nbctl ls-add ls2 > > + > > +# Connect ls1 to R1 > > +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > > +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > > + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" > > + > > +# Connect ls2 to R1 > > +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > > +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > > + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" > > + > > +# Create logical port ls1-lp1 in ls1 > > +ovn-nbctl lsp-add ls1 ls1-lp1 \ > > +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > > + > > +# Create logical port ls1-lp2 in ls1 > > +ovn-nbctl lsp-add ls1 ls1-lp2 \ > > +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > > + > > +# Create logical port ls2-lp1 in ls2 > > +ovn-nbctl lsp-add ls2 ls2-lp1 \ > > +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > > + > > +# Create logical port ls2-lp2 in ls2 > > +ovn-nbctl lsp-add ls2 ls2-lp2 \ > > +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > > + > > +ovn-nbctl lsp-add ls1 ln-public > > +ovn-nbctl lsp-set-type ln-public localnet > > +ovn-nbctl lsp-set-addresses ln-public unknown > > +ovn-nbctl lsp-set-options ln-public network_name=public > > + > > +# Create 2 hypervisors and create OVS ports corresponding to logical > ports for hv1. > > +net_add n1 > > + > > +sim_add hv1 > > +as hv1 > > +ovs-vsctl add-br br-phys -- set bridge br-phys > other-config:hwaddr=\"00:00:00:01:02:00\" > > +ovn_attach n1 br-phys 192.168.1.11 > > + > > +net_add n2 > > + > > +sim_add hv2 > > +as hv2 > > +ovs-vsctl add-br br-phys -- set bridge br-phys > other-config:hwaddr=\"00:00:00:02:02:00\" > > +ovn_attach n2 br-phys 192.168.1.12 > > + > > +OVN_POPULATE_ARP > > + > > +as hv1 > > + > > +ovs-vsctl -- add-port br-int vif1 -- \ > > + set interface vif1 external-ids:iface-id=ls1-lp1 > > + > > +ovs-vsctl -- add-port br-int vif2 -- \ > > + set interface vif2 external-ids:iface-id=ls2-lp1 > > + > > +ovs-vsctl -- add-port br-int vif3 -- \ > > + set interface vif3 external-ids:iface-id=ls1-lp2 > > + > > +ovs-vsctl -- add-port br-int vif4 -- \ > > + set interface vif4 external-ids:iface-id=ls2-lp2 > > + > > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > > + > > +# Allow some time for ovn-northd and ovn-controller to catch up. > > +wait_for_ports_up > > +check ovn-nbctl --wait=hv sync > > +ovn-nbctl dump-flows > sbflows > > +AT_CAPTURE_FILE([sbflows]) > > + > > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl --wait=hv sync > > +origA=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > > + > > +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > > +check ovn-nbctl --wait=hv sync > > +origB=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > > + > > +check ovn-nbctl --wait=hv sync > > + > > +as hv1 > > +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > + > > +check ovn-nbctl mirror-del > > +check ovn-nbctl --wait=hv sync > > + > > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > > +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > > + > > +# Attaches multiple mirrors > > + > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > + > > +AT_CHECK([test "$orig1" = "$new1"], [0], []) > > + > > +AT_CHECK([test "$orig2" = "$new2"], [0], []) > > + > > +# Equal detaches and attaches > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > > + > > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > + > > +# Make sure that ovn-controller has not asserted. > > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > + > > +AT_CHECK([test "$orig1" = "$new2"], [0], []) > > + > > +AT_CHECK([test "$orig2" = "$new1"], [0], []) > > + > > +# Detaches more than attaches > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 > > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 > > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 > > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > +# Make sure that ovn-controller has not asserted. > > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > + > > +AT_CHECK([test "$origA" = "$new1"], [0], []) > > + > > +AT_CHECK([test "$origB" = "$new2"], [0], []) > > + > > +# Attaches more than detaches > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > +# Make sure that ovn-controller has not asserted. > > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > + > > +AT_CHECK([test "$orig1" = "$new2"], [0], []) > > + > > +AT_CHECK([test "$orig2" = "$new1"], [0], []) > > + > > +# Detaches all > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 > > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 > > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > +check ovn-nbctl --wait=hv sync > > + > > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) > > + > > +check ovn-nbctl mirror-add mirror2 gre 2 to-lport 192.168.1.12 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 > > +check ovn-nbctl --wait=hv sync > > + > > +# Attaches SAME port to multiple mirrors > > +# and detaches it from existing mirror. > > +# More attach than detach > > + > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror2 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > + > > +AT_CHECK([test "$origA" = "$new1"], [0], []) > > +AT_CHECK([test "$origA" = "$new2"], [0], []) > > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror2 | wc > -l)]) > > + > > +check ovn-nbctl mirror-add mirror3 gre 3 to-lport 192.168.1.12 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror3 > > +check ovn-nbctl --wait=hv sync > > + > > +# Detaches SAME port from multiple mirrors > > +# and attaches it to existing mirror. > > +# More detach than attach > > + > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror3 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror1 select_dst_port) > > +new2=$(ovs-vsctl get Mirror mirror2 select_dst_port) > > + > > +AT_CHECK([test "$origA" = "$new1"], [0], []) > > +AT_CHECK([test "$origA" = "$new2"], [0], []) > > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror0 | wc > -l)]) > > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror3 | wc > -l)]) > > + > > +check ovn-nbctl mirror-del > > +check ovn-nbctl --wait=hv sync > > +check ovn-nbctl mirror-add mirror0 erspan 0 to-lport 192.168.1.12 > > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > > +check ovn-nbctl --wait=hv sync > > + > > +# Make sure different fields of mirror resource set from OVN > > +# propagates to OVS correctly > > + > > +check as hv1 ovn-appctl -t ovn-controller debug/pause > > +check ovn-nbctl set mirror . filter=from-lport > > +check ovn-nbctl set mirror . type=gre > > +check ovn-nbctl set mirror . index=123 > > +check as hv1 ovn-appctl -t ovn-controller debug/resume > > + > > +check ovn-nbctl --wait=hv sync > > + > > +as hv1 > > +new1=$(ovs-vsctl get Mirror mirror0 select_src_port) > > +AT_CHECK([test "$origA" = "$new1"], [0], []) > > +type=$(ovs-vsctl get Interface ovn-mirror0 type) > > +OVS_WAIT_UNTIL([test "gre" = "$type"]) > > +index=$(ovs-vsctl get Interface ovn-mirror0 options:key | grep 123 | wc > -l) > > +OVS_WAIT_UNTIL([test "1" = "$index"]) > > + > > +OVN_CLEANUP([hv1],[hv2]) > > +AT_CLEANUP > > +]) > > > > OVN_FOR_EACH_NORTHD([ > > AT_SETUP([Port Groups]) > >
diff --git a/NEWS b/NEWS index 6c4573b50..dbffcac0f 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,7 @@ OVN v22.09.0 - 16 Sep 2022 any of LR's LRP IP, there is no need to create SNAT entry. Now such traffic destined to LRP IP is not dropped. - Bump python version required for building OVN to 3.6. + - Added Support for Remote Port Mirroring. OVN v22.06.0 - 03 Jun 2022 -------------------------- diff --git a/controller/automake.mk b/controller/automake.mk index c2ab1bbe6..334672b4d 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \ controller/ovsport.h \ controller/ovsport.c \ controller/vif-plug.h \ - controller/vif-plug.c + controller/vif-plug.c \ + controller/mirror.h \ + controller/mirror.c controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la man_MANS += controller/ovn-controller.8 diff --git a/controller/mirror.c b/controller/mirror.c new file mode 100644 index 000000000..ac9d3eea5 --- /dev/null +++ b/controller/mirror.c @@ -0,0 +1,416 @@ +/* Copyright (c) 2022 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <unistd.h> + +/* library headers */ +#include "lib/sset.h" +#include "lib/util.h" + +/* OVS includes. */ +#include "lib/vswitch-idl.h" +#include "include/openvswitch/shash.h" +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "binding.h" +#include "lib/ovn-sb-idl.h" +#include "mirror.h" + +VLOG_DEFINE_THIS_MODULE(port_mirror); + +struct ovn_mirror { + char *name; + const struct sbrec_mirror *sb_mirror; + const struct ovsrec_mirror *ovs_mirror; + struct ovs_list mirror_src_lports; + struct ovs_list mirror_dst_lports; +}; + +struct mirror_lport { + struct ovs_list list_node; + + struct local_binding *lbinding; +}; + +static struct ovn_mirror *ovn_mirror_create(char *mirror_name); +static void ovn_mirror_add(struct shash *ovn_mirrors, + struct ovn_mirror *); +static struct ovn_mirror *ovn_mirror_find(struct shash *ovn_mirrors, + const char *mirror_name); +static void ovn_mirror_delete(struct ovn_mirror *); +static void ovn_mirror_add_lport(struct ovn_mirror *, struct local_binding *); +static void sync_ovn_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, + const struct ovsrec_bridge *); + +static void create_ovs_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, + const struct ovsrec_bridge *); +static void sync_ovs_mirror_ports(struct ovn_mirror *, + const struct ovsrec_bridge *); +static void delete_ovs_mirror(struct ovn_mirror *, + const struct ovsrec_bridge *); +static bool should_delete_ovs_mirror(struct ovn_mirror *); + +static const struct ovsrec_port *get_iface_port( + const struct ovsrec_interface *, const struct ovsrec_bridge *); + + +void +mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl) +{ + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors); + + 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); + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_external_ids); +} + +void +mirror_init(void) +{ +} + +void +mirror_destroy(void) +{ +} + +void +mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_mirror_table *ovs_mirror_table, + const struct sbrec_mirror_table *sb_mirror_table, + const struct ovsrec_bridge *br_int, + struct shash *local_bindings) +{ + if (!ovs_idl_txn) { + return; + } + + struct shash ovn_mirrors = SHASH_INITIALIZER(&ovn_mirrors); + struct shash tmp_mirrors = SHASH_INITIALIZER(&tmp_mirrors); + + /* Iterate through sb mirrors and build the 'ovn_mirrors'. */ + const struct sbrec_mirror *sb_mirror; + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, sb_mirror_table) { + struct ovn_mirror *m = ovn_mirror_create(sb_mirror->name); + m->sb_mirror = sb_mirror; + ovn_mirror_add(&ovn_mirrors, m); + } + + /* Iterate through ovs mirrors and add to the 'ovn_mirrors'. */ + const struct ovsrec_mirror *ovs_mirror; + OVSREC_MIRROR_TABLE_FOR_EACH (ovs_mirror, ovs_mirror_table) { + bool ovn_owned_mirror = smap_get_bool(&ovs_mirror->external_ids, + "ovn-owned", false); + if (!ovn_owned_mirror) { + continue; + } + + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, ovs_mirror->name); + if (!m) { + m = ovn_mirror_create(ovs_mirror->name); + shash_add(&tmp_mirrors, ovs_mirror->name, m); + } + m->ovs_mirror = ovs_mirror; + } + + if (shash_is_empty(&ovn_mirrors)) { + shash_destroy(&ovn_mirrors); + if (shash_is_empty(&tmp_mirrors)) { + shash_destroy(&tmp_mirrors); + return; + } else { + goto cleanup; + } + } + + /* Iterate through the local bindings and if the local binding's 'pb' has + * mirrors associated, add it to the ovn_mirror. */ + struct shash_node *node; + SHASH_FOR_EACH (node, local_bindings) { + struct local_binding *lbinding = node->data; + const struct sbrec_port_binding *pb = + local_binding_get_primary_pb(local_bindings, lbinding->name); + if (!pb || !pb->n_mirror_rules) { + continue; + } + + for (size_t i = 0; i < pb->n_mirror_rules; i++) { + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, + pb->mirror_rules[i]->name); + ovs_assert(m); + ovn_mirror_add_lport(m, lbinding); + } + } + + /* Iterate through the built 'ovn_mirrors' and + * sync with the local ovsdb i.e. + * create/update or delete the ovsrec mirror(s). */ + SHASH_FOR_EACH (node, &ovn_mirrors) { + struct ovn_mirror *m = node->data; + sync_ovn_mirror(m, ovs_idl_txn, br_int); + } + + SHASH_FOR_EACH_SAFE (node, &ovn_mirrors) { + ovn_mirror_delete(node->data); + shash_delete(&ovn_mirrors, node); + } + + shash_destroy(&ovn_mirrors); + +cleanup: + SHASH_FOR_EACH_SAFE (node, &tmp_mirrors) { + ovn_mirror_delete(node->data); + shash_delete(&tmp_mirrors, node); + } + + shash_destroy(&tmp_mirrors); +} + +/* Static functions. */ +static struct ovn_mirror * +ovn_mirror_create(char *mirror_name) +{ + struct ovn_mirror *m = xzalloc(sizeof *m); + m->name = xstrdup(mirror_name); + ovs_list_init(&m->mirror_src_lports); + ovs_list_init(&m->mirror_dst_lports); + return m; +} + +static void +ovn_mirror_add(struct shash *ovn_mirrors, struct ovn_mirror *m) +{ + shash_add(ovn_mirrors, m->sb_mirror->name, m); +} + +static struct ovn_mirror * +ovn_mirror_find(struct shash *ovn_mirrors, const char *mirror_name) +{ + return shash_find_data(ovn_mirrors, mirror_name); +} + +static void +ovn_mirror_delete(struct ovn_mirror *m) +{ + free(m->name); + struct mirror_lport *m_lport; + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_src_lports) { + free(m_lport); + } + + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_dst_lports) { + free(m_lport); + } + + free(m); +} + +static void +ovn_mirror_add_lport(struct ovn_mirror *m, struct local_binding *lbinding) +{ + struct mirror_lport *m_lport = xzalloc(sizeof *m_lport); + m_lport->lbinding = lbinding; + if (!strcmp(m->sb_mirror->filter, "from-lport")) { + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); + } else if (!strcmp(m->sb_mirror->filter, "to-lport")) { + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); + } else { + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); + m_lport = xzalloc(sizeof *m_lport); + m_lport->lbinding = lbinding; + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); + } +} + +static void +create_and_set_options(struct ovsrec_interface *iface, + const struct sbrec_mirror *sb_mirror) +{ + struct smap options = SMAP_INITIALIZER(&options); + char *key; + + key = xasprintf("%ld", (long int) sb_mirror->index); + smap_add(&options, "remote_ip", sb_mirror->sink); + smap_add(&options, "key", key); + if (!strcmp(sb_mirror->type, "erspan")) { + /* Set the ERSPAN index */ + smap_add(&options, "erspan_idx", key); + smap_add(&options, "erspan_ver", "1"); + } + ovsrec_interface_set_options(iface, &options); + + free(key); + smap_destroy(&options); +} + +static void +check_and_update_interface_table(const struct sbrec_mirror *sb_mirror, + const struct ovsrec_mirror *ovs_mirror) +{ + char *type; + struct ovsrec_interface *iface = + ovs_mirror->output_port->interfaces[0]; + struct smap *opts = &iface->options; + const char *erspan_ver = smap_get(opts, "erspan_ver"); + if (erspan_ver) { + type = "erspan"; + } else { + type = "gre"; + } + if (strcmp(type, sb_mirror->type)) { + ovsrec_interface_set_type(iface, sb_mirror->type); + } + create_and_set_options(iface, sb_mirror); +} + +static void +sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge *br_int) +{ + if (should_delete_ovs_mirror(m)) { + /* Delete the ovsrec mirror. */ + delete_ovs_mirror(m, br_int); + return; + } + + if (ovs_list_is_empty(&m->mirror_src_lports) && + ovs_list_is_empty(&m->mirror_dst_lports)) { + /* Nothing to do. */ + return; + } + + if (m->sb_mirror && !m->ovs_mirror) { + create_ovs_mirror(m, ovs_idl_txn, br_int); + } else { + check_and_update_interface_table(m->sb_mirror, m->ovs_mirror); + } + + sync_ovs_mirror_ports(m, br_int); +} + +static bool +should_delete_ovs_mirror(struct ovn_mirror *m) +{ + if (!m->ovs_mirror) { + return false; + } + + if (m->ovs_mirror && !m->sb_mirror) { + return true; + } + + return (ovs_list_is_empty(&m->mirror_src_lports) && + ovs_list_is_empty(&m->mirror_dst_lports)); +} + +static const struct ovsrec_port * +get_iface_port(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 +create_ovs_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge *br_int) +{ + struct ovsrec_interface *iface = ovsrec_interface_insert(ovs_idl_txn); + char *port_name = xasprintf("ovn-%s", m->name); + + ovsrec_interface_set_name(iface, port_name); + ovsrec_interface_set_type(iface, m->sb_mirror->type); + create_and_set_options(iface, m->sb_mirror); + + struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn); + ovsrec_port_set_name(port, port_name); + ovsrec_port_set_interfaces(port, &iface, 1); + ovsrec_bridge_update_ports_addvalue(br_int, port); + + free(port_name); + + m->ovs_mirror = ovsrec_mirror_insert(ovs_idl_txn); + ovsrec_mirror_set_name(m->ovs_mirror, m->name); + ovsrec_mirror_set_output_port(m->ovs_mirror, port); + + const struct smap external_ids = + SMAP_CONST1(&external_ids, "ovn-owned", "true"); + ovsrec_mirror_set_external_ids(m->ovs_mirror, &external_ids); + + ovsrec_bridge_update_mirrors_addvalue(br_int, m->ovs_mirror); +} + +static void +sync_ovs_mirror_ports(struct ovn_mirror *m, const struct ovsrec_bridge *br_int) +{ + struct mirror_lport *m_lport; + + if (ovs_list_is_empty(&m->mirror_src_lports)) { + ovsrec_mirror_set_select_src_port(m->ovs_mirror, NULL, 0); + } else { + size_t n_lports = ovs_list_size(&m->mirror_src_lports); + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * n_lports); + + size_t i = 0; + LIST_FOR_EACH (m_lport, list_node, &m->mirror_src_lports) { + const struct ovsrec_port *p = + get_iface_port(m_lport->lbinding->iface, br_int); + ovs_assert(p); + ovs_ports[i++] = (struct ovsrec_port *) p; + } + + ovsrec_mirror_set_select_src_port(m->ovs_mirror, ovs_ports, n_lports); + free(ovs_ports); + } + + if (ovs_list_is_empty(&m->mirror_dst_lports)) { + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, NULL, 0); + } else { + size_t n_lports = ovs_list_size(&m->mirror_dst_lports); + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * n_lports); + + size_t i = 0; + LIST_FOR_EACH (m_lport, list_node, &m->mirror_dst_lports) { + const struct ovsrec_port *p = + get_iface_port(m_lport->lbinding->iface, br_int); + ovs_assert(p); + ovs_ports[i++] = (struct ovsrec_port *) p; + } + + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, ovs_ports, n_lports); + free(ovs_ports); + } +} + +static void +delete_ovs_mirror(struct ovn_mirror *m, const struct ovsrec_bridge *br_int) +{ + ovsrec_bridge_update_ports_delvalue(br_int, m->ovs_mirror->output_port); + ovsrec_bridge_update_mirrors_delvalue(br_int, m->ovs_mirror); + ovsrec_port_delete(m->ovs_mirror->output_port); + ovsrec_mirror_delete(m->ovs_mirror); +} diff --git a/controller/mirror.h b/controller/mirror.h new file mode 100644 index 000000000..a79de109d --- /dev/null +++ b/controller/mirror.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2022 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_MIRROR_H +#define OVN_MIRROR_H 1 + +struct ovsdb_idl_txn; +struct ovsrec_mirror_table; +struct sbrec_mirror_table; +struct ovsrec_bridge; +struct shash; + +void mirror_register_ovs_idl(struct ovsdb_idl *); +void mirror_init(void); +void mirror_destroy(void); +void mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_mirror_table *, + const struct sbrec_mirror_table *, + const struct ovsrec_bridge *, + struct shash *local_bindings); +#endif diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 0752a71ad..0e1d31dbd 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -78,6 +78,7 @@ #include "lib/inc-proc-eng.h" #include "lib/ovn-l7.h" #include "hmapx.h" +#include "mirror.h" VLOG_DEFINE_THIS_MODULE(main); @@ -994,6 +995,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids); + mirror_register_ovs_idl(ovs_idl); } #define SB_NODES \ @@ -3619,6 +3621,7 @@ main(int argc, char *argv[]) patch_init(); pinctrl_init(); lflow_init(); + mirror_init(); vif_plug_provider_initialize(); /* Connect to OVS OVSDB instance. */ @@ -4195,34 +4198,35 @@ main(int argc, char *argv[]) stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, time_msec()); - if (ovnsb_idl_txn) { - if (ofctrl_has_backlog()) { - /* When there are in-flight messages pending to - * ovs-vswitchd, we should hold on recomputing so - * that the previous flow installations won't be - * delayed. However, we still want to try if - * recompute is not needed and we can quickly - * incrementally process the new changes, to avoid - * unnecessarily forced recomputes later on. This - * is because the OVSDB change tracker cannot - * preserve tracked changes across iterations. If - * change tracking is improved, we can simply skip - * this round of engine_run and continue processing - * acculated changes incrementally later when - * ofctrl_has_backlog() returns false. */ - engine_run(false); - } else { - engine_run(true); - } - } else { - /* Even if there's no SB DB transaction available, + + bool allow_engine_recompute = true; + + if (!ovnsb_idl_txn || ofctrl_has_backlog()) { + /* When there are in-flight messages pending to + * ovs-vswitchd, we should hold on recomputing so + * that the previous flow installations won't be + * delayed. However, we still want to try if + * recompute is not needed and we can quickly + * incrementally process the new changes, to avoid + * unnecessarily forced recomputes later on. This + * is because the OVSDB change tracker cannot + * preserve tracked changes across iterations. If + * change tracking is improved, we can simply skip + * this round of engine_run and continue processing + * acculated changes incrementally later when + * ofctrl_has_backlog() returns false. */ + + /* Even if there's no SB/OVS DB transaction available, * try to run the engine so that we can handle any * incremental changes that don't require a recompute. * If a recompute is required, the engine will abort, * triggerring a full run in the next iteration. */ - engine_run(false); + allow_engine_recompute = false; } + + engine_run(allow_engine_recompute); + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, time_msec()); if (engine_has_updated()) { @@ -4312,6 +4316,11 @@ main(int argc, char *argv[]) &runtime_data->local_active_ports_ras); stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME, time_msec()); + mirror_run(ovs_idl_txn, + ovsrec_mirror_table_get(ovs_idl_loop.idl), + sbrec_mirror_table_get(ovnsb_idl_loop.idl), + br_int, + &runtime_data->lbinding_data.bindings); /* Updating monitor conditions if runtime data or * logical datapath goups changed. */ if (engine_node_changed(&en_runtime_data) @@ -4555,6 +4564,7 @@ loop_done: pinctrl_destroy(); binding_destroy(); patch_destroy(); + mirror_destroy(); if_status_mgr_destroy(if_mgr); shash_destroy(&vif_plug_deleted_iface_ids); shash_destroy(&vif_plug_changed_iface_ids); diff --git a/tests/ovn.at b/tests/ovn.at index 0ef536509..1769f4906 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -16237,6 +16237,520 @@ OVN_CLEANUP([hv1], [hv2]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Mirror]) +AT_KEYWORDS([Mirror]) +ovn_start + +# Logical network: +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, +# and has switch ls2 (172.16.1.0/24) connected to it. + +ovn-nbctl lr-add R1 + +ovn-nbctl ls-add ls1 +ovn-nbctl ls-add ls2 + +# Connect ls1 to R1 +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" + +# Connect ls2 to R1 +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" + +# Create logical port ls1-lp1 in ls1 +ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" + +# Create logical port ls2-lp1 in ls2 +ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" + +ovn-nbctl lsp-add ls1 ln-public +ovn-nbctl lsp-set-type ln-public localnet +ovn-nbctl lsp-set-addresses ln-public unknown +ovn-nbctl lsp-set-options ln-public network_name=public + +# Create one hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:01:02:00\" +ovn_attach n1 br-phys 192.168.1.11 + +ovs-vsctl -- add-port br-int vif1 -- \ + set interface vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +ovs-vsctl -- add-port br-int vif2 -- \ + set interface vif2 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap \ + ofport-request=1 + +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync +ovn-nbctl dump-flows > sbflows +AT_CAPTURE_FILE([sbflows]) + +for i in 1 2; do + : > vif$i.expected +done + +net_add n2 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" +ovn_attach n2 br-phys 192.168.1.12 + +OVN_POPULATE_ARP + +as hv1 + +# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] ENCAP_TYPE FILTER +# +# Causes a packet to be received on INPORT. The packet is an ICMPv4 +# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and +# ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are +# provided, then it should be the ip and icmp checksums of the packet +# responded; otherwise, no reply is expected. +# In the absence of an ip checksum calculation helpers, this relies +# on the caller to provide the checksums for the ip and icmp headers. +# XXX This should be more systematic. +# +# INPORT is an lport number, e.g. 11 for vif11. +# ETH_SRC and ETH_DST are each 12 hex digits. +# IPV4_SRC and IPV4_DST are each 8 hex digits. +# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits. +# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits. +# ENCAP_TYPE - gre or erspan +# FILTER - Mirror Filter - to-lport / from-lport +test_ipv4_icmp_request() { + local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 ip_chksum=$6 icmp_chksum=$7 + local exp_ip_chksum=$8 exp_icmp_chksum=$9 mirror_encap_type=${10} mirror_filter=${11} + shift; shift; shift; shift; shift; shift; shift + shift; shift; shift; shift; + + # Use ttl to exercise section 4.2.2.9 of RFC1812 + local ip_ttl=02 + local icmp_id=5fbf + local icmp_seq=0001 + local icmp_data=$(seq 1 56 | xargs printf "%02x") + local icmp_type_code_request=0800 + local icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} + local packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload} + + as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet + + # Expect to receive the reply, if any. In same port where packet was sent. + # Note: src and dst fields are expected to be reversed. + local icmp_type_code_response=0000 + local reply_icmp_ttl=fe + local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} + local reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload} + echo $reply >> vif$inport.expected + local remote_mac=000000020200 + local local_mac=000000010200 + if test ${mirror_encap_type} = "gre" ; then + local ipv4_gre=4500007e00004000402fb6e9c0a8010bc0a8010c2000655800000000 + if test ${mirror_filter} = "to-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${reply} + elif test ${mirror_filter} = "from-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${packet} + fi + elif test ${mirror_encap_type} = "erspan" ; then + local ipv4_erspan=4500008600004000402fb6e1c0a8010bc0a8010c + local erspan_seq0=100088be000000001000000000000000 + local erspan_seq1=100088be000000011000000000000000 + if test ${mirror_filter} = "to-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq0}${reply} + elif test ${mirror_filter} = "from-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq1}${packet} + fi + fi + echo $mirror >> br-phys_n1.expected + +} + +# Set IPs +rtr_l2_ip=$(ip_to_hex 172 16 1 1) +l1_ip=$(ip_to_hex 192 168 1 2) + +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 + +# Send ping packet and check for mirrored packet of the reply +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "to-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 +rm -f br-phys_n1.expected +rm -f vif1.expected + +check ovn-nbctl set mirror . type=erspan + +# Send ping packet and check for mirrored packet of the reply +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "to-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 +rm -f br-phys_n1.expected +rm -f vif1.expected + +check ovn-nbctl set mirror . filter=from-lport + +# Send ping packet and check for mirrored packet of the request +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "from-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 +rm -f br-phys_n1.expected +rm -f vif1.expected + +check ovn-nbctl set mirror . type=gre + +# Send ping packet and check for mirrored packet of the request +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "from-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +echo "---------OVN NB Mirror-----" +ovn-nbctl mirror-list + +echo "---------OVS Mirror----" +ovs-vsctl list Mirror + +echo "-----------------------" + +echo "Verifying Mirror deletion in OVS" +# Set vif1 iface-id such that OVN releases port binding +check ovs-vsctl set interface vif1 external_ids:iface-id=foo +check ovn-nbctl --wait=hv sync + +AT_CHECK([as hv1 ovs-vsctl list Mirror], [0], [dnl +]) + +# Set vif1 iface-id back to ls1-lp1 +check ovs-vsctl set interface vif1 external_ids:iface-id=ls1-lp1 +check ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([test $(as hv1 ovs-vsctl get Mirror mirror0 name) = "mirror0"]) + +# Delete vif1 so that OVN releases port binding +check ovs-vsctl del-port br-int vif1 +check ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) + +OVN_CLEANUP([hv1], [hv2]) +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Mirror test bulk updates]) +AT_KEYWORDS([Mirror test bulk updates]) +ovn_start + +# Logical network: +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, +# and has switch ls2 (172.16.1.0/24) connected to it. + +ovn-nbctl lr-add R1 + +ovn-nbctl ls-add ls1 +ovn-nbctl ls-add ls2 + +# Connect ls1 to R1 +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" + +# Connect ls2 to R1 +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" + +# Create logical port ls1-lp1 in ls1 +ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" + +# Create logical port ls1-lp2 in ls1 +ovn-nbctl lsp-add ls1 ls1-lp2 \ +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" + +# Create logical port ls2-lp1 in ls2 +ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" + +# Create logical port ls2-lp2 in ls2 +ovn-nbctl lsp-add ls2 ls2-lp2 \ +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" + +ovn-nbctl lsp-add ls1 ln-public +ovn-nbctl lsp-set-type ln-public localnet +ovn-nbctl lsp-set-addresses ln-public unknown +ovn-nbctl lsp-set-options ln-public network_name=public + +# Create 2 hypervisors and create OVS ports corresponding to logical ports for hv1. +net_add n1 + +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:01:02:00\" +ovn_attach n1 br-phys 192.168.1.11 + +net_add n2 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" +ovn_attach n2 br-phys 192.168.1.12 + +OVN_POPULATE_ARP + +as hv1 + +ovs-vsctl -- add-port br-int vif1 -- \ + set interface vif1 external-ids:iface-id=ls1-lp1 + +ovs-vsctl -- add-port br-int vif2 -- \ + set interface vif2 external-ids:iface-id=ls2-lp1 + +ovs-vsctl -- add-port br-int vif3 -- \ + set interface vif3 external-ids:iface-id=ls1-lp2 + +ovs-vsctl -- add-port br-int vif4 -- \ + set interface vif4 external-ids:iface-id=ls2-lp2 + +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync +ovn-nbctl dump-flows > sbflows +AT_CAPTURE_FILE([sbflows]) + +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl --wait=hv sync +origA=$(ovs-vsctl get Mirror mirror0 select_dst_port) +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 + +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 +check ovn-nbctl --wait=hv sync +origB=$(ovs-vsctl get Mirror mirror1 select_dst_port) +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 + +check ovn-nbctl --wait=hv sync + +as hv1 +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +check ovn-nbctl mirror-del +check ovn-nbctl --wait=hv sync + +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 + +# Attaches multiple mirrors + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$orig1" = "$new1"], [0], []) + +AT_CHECK([test "$orig2" = "$new2"], [0], []) + +# Equal detaches and attaches +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 + +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +# Make sure that ovn-controller has not asserted. +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$orig1" = "$new2"], [0], []) + +AT_CHECK([test "$orig2" = "$new1"], [0], []) + +# Detaches more than attaches +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync +# Make sure that ovn-controller has not asserted. +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$origA" = "$new1"], [0], []) + +AT_CHECK([test "$origB" = "$new2"], [0], []) + +# Attaches more than detaches +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync +# Make sure that ovn-controller has not asserted. +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$orig1" = "$new2"], [0], []) + +AT_CHECK([test "$orig2" = "$new1"], [0], []) + +# Detaches all +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume +check ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) + +check ovn-nbctl mirror-add mirror2 gre 2 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 +check ovn-nbctl --wait=hv sync + +# Attaches SAME port to multiple mirrors +# and detaches it from existing mirror. +# More attach than detach + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror2 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$origA" = "$new1"], [0], []) +AT_CHECK([test "$origA" = "$new2"], [0], []) +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror2 | wc -l)]) + +check ovn-nbctl mirror-add mirror3 gre 3 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror3 +check ovn-nbctl --wait=hv sync + +# Detaches SAME port from multiple mirrors +# and attaches it to existing mirror. +# More detach than attach + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror3 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror1 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror2 select_dst_port) + +AT_CHECK([test "$origA" = "$new1"], [0], []) +AT_CHECK([test "$origA" = "$new2"], [0], []) +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror0 | wc -l)]) +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror3 | wc -l)]) + +check ovn-nbctl mirror-del +check ovn-nbctl --wait=hv sync +check ovn-nbctl mirror-add mirror0 erspan 0 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl --wait=hv sync + +# Make sure different fields of mirror resource set from OVN +# propagates to OVS correctly + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl set mirror . filter=from-lport +check ovn-nbctl set mirror . type=gre +check ovn-nbctl set mirror . index=123 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_src_port) +AT_CHECK([test "$origA" = "$new1"], [0], []) +type=$(ovs-vsctl get Interface ovn-mirror0 type) +OVS_WAIT_UNTIL([test "gre" = "$type"]) +index=$(ovs-vsctl get Interface ovn-mirror0 options:key | grep 123 | wc -l) +OVS_WAIT_UNTIL([test "1" = "$index"]) + +OVN_CLEANUP([hv1],[hv2]) +AT_CLEANUP +]) OVN_FOR_EACH_NORTHD([ AT_SETUP([Port Groups])