Acked-By: Ihar Hrachyshka <[email protected]> On Tue, Dec 13, 2022 at 2:33 PM Abhiram R N <[email protected]> wrote: > > In order to support Remote Port Mirroring > added the required schemas in NB and related xml. > Also, ovn-nbctl.c changes are added. > Futher added test cases to test nbctl commands. > > Co-authored-by: Veda Barrenkala <[email protected]> > Signed-off-by: Veda Barrenkala <[email protected]> > Signed-off-by: Abhiram R N <[email protected]> > Acked-By: Ihar Hrachyshka <[email protected]> > --- > ovn-nb.ovsschema | 25 ++- > ovn-nb.xml | 55 +++++++ > tests/ovn-nbctl.at | 102 ++++++++++++ > utilities/ovn-nbctl.8.xml | 51 ++++++ > utilities/ovn-nbctl.c | 334 ++++++++++++++++++++++++++++++++++++++ > 5 files changed, 565 insertions(+), 2 deletions(-) > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema > index 6f9d38f47..4836a219f 100644 > --- a/ovn-nb.ovsschema > +++ b/ovn-nb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Northbound", > - "version": "6.4.0", > - "cksum": "3512158873 32360", > + "version": "7.0.0", > + "cksum": "94023179 33468", > "tables": { > "NB_Global": { > "columns": { > @@ -132,6 +132,11 @@ > "refType": "weak"}, > "min": 0, > "max": 1}}, > + "mirror_rules": {"type": {"key": {"type": "uuid", > + "refTable": "Mirror", > + "refType": "weak"}, > + "min": 0, > + "max": "unlimited"}}, > "ha_chassis_group": { > "type": {"key": {"type": "uuid", > "refTable": "HA_Chassis_Group", > @@ -301,6 +306,22 @@ > "type": {"key": "string", "value": "string", > "min": 0, "max": "unlimited"}}}, > "isRoot": false}, > + "Mirror": { > + "columns": { > + "name": {"type": "string"}, > + "filter": {"type": {"key": {"type": "string", > + "enum": ["set", ["from-lport", > + "to-lport"]]}}}, > + "sink":{"type": "string"}, > + "type": {"type": {"key": {"type": "string", > + "enum": ["set", ["gre", > + "erspan"]]}}}, > + "index": {"type": "integer"}, > + "external_ids": { > + "type": {"key": "string", "value": "string", > + "min": 0, "max": "unlimited"}}}, > + "indexes": [["name"]], > + "isRoot": true}, > "Meter": { > "columns": { > "name": {"type": "string"}, > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 0edc3da96..c2d05c6ef 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -1582,6 +1582,11 @@ > </column> > </group> > > + <column name="mirror_rules"> > + Mirror rules that apply to logical switch port which is the source. > + Please see the <ref table="Mirror"/> table. > + </column> > + > <column name="ha_chassis_group"> > References a row in the OVN Northbound database's > <ref table="HA_Chassis_Group" db="OVN_Northbound"/> table. > @@ -2587,6 +2592,56 @@ or > </column> > </table> > > + <table name="Mirror" title="Mirror Entry"> > + <p> > + Each row in this table represents one Mirror that can be used for > + port mirroring. These Mirrors are referenced by the > + <ref column="mirror_rules" table="Logical_Switch_Port"/> column in > + the <ref table="Logical_Switch_Port"/> table. > + </p> > + > + <column name="name"> > + <p> > + Represents the name of the mirror. > + </p> > + </column> > + > + <column name="filter"> > + <p> > + The value of this field represents selection criteria of the mirror. > + Supported values for filter to-lport / from-lport > + to-lport - to mirror packets coming into logical port > + from-lport - to mirror packets going out of logical port. > + </p> > + </column> > + > + <column name="sink"> > + <p> > + The value of this field represents the destination/sink of the > mirror. > + The value it takes is an IP address of the sink port. > + </p> > + </column> > + > + <column name="type"> > + <p> > + The value of this field represents the type of the tunnel used for > + sending the mirrored packets. Supported Tunnel types gre and erspan > + </p> > + </column> > + > + <column name="index"> > + <p> > + The value of this field represents the tunnel ID. Depending on the > + tunnel type configured, GRE key value if type GRE and erspan_idx > value > + if ERSPAN > + </p> > + </column> > + > + <column name="external_ids"> > + See <em>External IDs</em> at the beginning of this document. > + </column> > + </table> > + > <table name="Meter" title="Meter entry"> > <p> > Each row in this table represents a meter that can be used for QoS or > diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at > index 91d8d338e..8885ac9fc 100644 > --- a/tests/ovn-nbctl.at > +++ b/tests/ovn-nbctl.at > @@ -435,6 +435,108 @@ AT_CHECK([ovn-nbctl meter-list], [0], [dnl > > dnl --------------------------------------------------------------------- > > +OVN_NBCTL_TEST([ovn_nbctl_mirrors], [mirrors], [ > +check ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.1 > +check ovn-nbctl mirror-add mirror2 erspan 1 to-lport 10.10.10.2 > +check ovn-nbctl mirror-add mirror3 gre 2 to-lport 10.10.10.3 > +check ovn-nbctl ls-add sw0 > +check ovn-nbctl lsp-add sw0 sw0-port1 > +check ovn-nbctl lsp-add sw0 sw0-port2 > +check ovn-nbctl lsp-add sw0 sw0-port3 > + > +dnl Add duplicate mirror name > +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.5], [1], > [], [stderr]) > +AT_CHECK([grep 'already exists' stderr], [0], [ignore]) > + > +dnl Attach invalid source port to mirror > +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port4 mirror3], [1], [], [stderr]) > +AT_CHECK([grep 'port name not found' stderr], [0], [ignore]) > + > +dnl Attach source port to invalid mirror > +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port3 mirror4], [1], [], [stderr]) > +AT_CHECK([grep 'mirror name not found' stderr], [0], [ignore]) > + > +mirror3uuid=$(fetch_column nb:Mirror _uuid name=mirror3) > +dnl Attach source port to mirror > +check ovn-nbctl lsp-attach-mirror sw0-port1 mirror3 > +check_column "$mirror3uuid" nb:Logical_Switch_Port mirror_rules > name=sw0-port1 > + > +dnl Attach one more source port to mirror > +check ovn-nbctl lsp-attach-mirror sw0-port3 mirror3 > +check_column "$mirror3uuid" nb:Logical_Switch_Port mirror_rules > name=sw0-port3 > + > +dnl Verify if multiple ports are attached to the same mirror properly > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > +mirror1: > + Type : gre > + Sink : 10.10.10.1 > + Filter : from-lport > + Index/Key: 0 > + > +mirror2: > + Type : erspan > + Sink : 10.10.10.2 > + Filter : to-lport > + Index/Key: 1 > + > +mirror3: > + Type : gre > + Sink : 10.10.10.3 > + Filter : to-lport > + Index/Key: 2 > + > +]) > + > +dnl Detach one source port from mirror > +check ovn-nbctl lsp-detach-mirror sw0-port3 mirror3 > + > +dnl Check if the detach happened from source properly > +check_column "" nb:Logical_Switch_Port mirror_rules name=sw0-port3 > + > +dnl Delete a single mirror which has source attached. > +check ovn-nbctl mirror-del mirror3 > + > +dnl Check if the detach happened from source properly > +check_column "" nb:Logical_Switch_Port mirror_rules name=sw0-port1 > + > +dnl Check if the mirror deleted properly > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > +mirror1: > + Type : gre > + Sink : 10.10.10.1 > + Filter : from-lport > + Index/Key: 0 > + > +mirror2: > + Type : erspan > + Sink : 10.10.10.2 > + Filter : to-lport > + Index/Key: 1 > + > +]) > + > +dnl Delete another mirror > +check ovn-nbctl mirror-del mirror2 > + > +dnl Update the Sink address > +check ovn-nbctl set mirror . sink=192.168.1.13 > + > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > +mirror1: > + Type : gre > + Sink : 192.168.1.13 > + Filter : from-lport > + Index/Key: 0 > + > +]) > + > +dnl Delete all mirrors > +check ovn-nbctl mirror-del > +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > +])]) > + > +dnl --------------------------------------------------------------------- > + > OVN_NBCTL_TEST([ovn_nbctl_nats], [NATs], [ > AT_CHECK([ovn-nbctl lr-add lr0]) > AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [], > diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml > index 5f9eb186b..92e10c012 100644 > --- a/utilities/ovn-nbctl.8.xml > +++ b/utilities/ovn-nbctl.8.xml > @@ -809,6 +809,15 @@ > Get the logical switch which the <var>port</var> belongs to. > </dd> > > + <dt><code>lsp-attach-mirror</code> <var>port</var> <var>m</var></dt> > + <dd> > + Attaches the mirror <var>m</var> to the logical port <var>port</var>. > + </dd> > + > + <dt><code>lsp-dettach-mirror</code> <var>port</var> <var>m</var></dt> > + <dd> > + Detaches the mirror <var>m</var> from the logical port > <var>port</var>. > + </dd> > </dl> > > <h2>Forwarding Group Commands</h2> > @@ -1534,6 +1543,48 @@ > </dd> > </dl> > > + <h2> Mirror commands</h2> > + <dl> > + <dt><code>mirror-add</code> <var>m</var> <var>type</var> > + <var>index</var> <var>filter</var> <var>dest</var></dt> > + <dd> > + <p> > + Creates a new mirror in the <code>Mirror</code> > + table with the name <code>m</code> with the below mandatory > + arguments. > + </p> > + > + <p> > + <var>type</var> specifies the mirror type - <code>gre</code> > + or <code>erspan</code>. > + </p> > + > + <p> > + <var>index</var> specifies the tunnel index value (which is > + an integer). > + </p> > + > + <p> > + <var>filter</var> specifies the mirror source selection. > + Can be <code>from-lport</code> or <code>to-lport</code>. > + </p> > + > + <p> > + <var>dest</var> specifies the mirror destination IP (v4 or v6). > + </p> > + </dd> > + > + <dt><code>mirror-del</code> <var>m</var></dt> > + <dd> > + Deletes the mirror <code>m</code>. > + </dd> > + > + <dt><code>mirror-list</code></dt> > + <dd> > + Lists the mirrors. > + </dd> > + </dl> > + > <h2>Synchronization Commands</h2> > > <dl> > diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c > index 3bc4a8d82..d25b60e78 100644 > --- a/utilities/ovn-nbctl.c > +++ b/utilities/ovn-nbctl.c > @@ -272,6 +272,19 @@ QoS commands:\n\ > remove QoS rules from SWITCH\n\ > qos-list SWITCH print QoS rules for SWITCH\n\ > \n\ > +Mirror commands:\n\ > + mirror-add NAME TYPE INDEX FILTER IP\n\ > + add a mirror with given name\n\ > + specify TYPE 'gre' or 'erspan'\n\ > + specify the tunnel INDEX value\n\ > + (indicates key if GRE\n\ > + erpsan_idx if ERSPAN)\n\ > + specify FILTER for mirroring selection\n\ > + 'to-lport' / 'from-lport'\n\ > + specify Sink / Destination i.e. Remote IP\n\ > + mirror-del [NAME] remove mirrors\n\ > + mirror-list print mirrors\n\ > +\n\ > Meter commands:\n\ > [--fair]\n\ > meter-add NAME ACTION RATE UNIT [BURST]\n\ > @@ -312,6 +325,8 @@ Logical switch port commands:\n\ > set dhcpv6 options for PORT\n\ > lsp-get-dhcpv6-options PORT get the dhcpv6 options for PORT\n\ > lsp-get-ls PORT get the logical switch which the port belongs > to\n\ > + lsp-attach-mirror PORT MIRROR attach source PORT to MIRROR\n\ > + lsp-detach-mirror PORT MIRROR detach source PORT from MIRROR\n\ > \n\ > Forwarding group commands:\n\ > [--liveness]\n\ > @@ -1686,6 +1701,126 @@ nbctl_pre_lsp_type(struct ctl_context *ctx) > ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_type); > } > > +static void > +nbctl_pre_lsp_mirror(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name); > + ovsdb_idl_add_column(ctx->idl, > + &nbrec_logical_switch_port_col_mirror_rules); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > +} > + > +static int > +mirror_cmp(const void *mirror1_, const void *mirror2_) > +{ > + const struct nbrec_mirror *const *mirror_1 = mirror1_; > + const struct nbrec_mirror *const *mirror_2 = mirror2_; > + > + const struct nbrec_mirror *mirror1 = *mirror_1; > + const struct nbrec_mirror *mirror2 = *mirror_2; > + > + return strcmp(mirror1->name, mirror2->name); > +} > + > +static char * OVS_WARN_UNUSED_RESULT > +mirror_by_name_or_uuid(struct ctl_context *ctx, const char *id, > + bool must_exist, > + const struct nbrec_mirror **mirror_p) > +{ > + const struct nbrec_mirror *mirror = NULL; > + *mirror_p = NULL; > + > + struct uuid mirror_uuid; > + bool is_uuid = uuid_from_string(&mirror_uuid, id); > + if (is_uuid) { > + mirror = nbrec_mirror_get_for_uuid(ctx->idl, &mirror_uuid); > + } > + > + if (!mirror) { > + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { > + if (!strcmp(mirror->name, id)) { > + break; > + } > + } > + } > + > + if (!mirror && must_exist) { > + return xasprintf("%s: mirror %s not found", > + id, is_uuid ? "UUID" : "name"); > + } > + > + *mirror_p = mirror; > + return NULL; > +} > + > +static void > +nbctl_lsp_attach_mirror(struct ctl_context *ctx) > +{ > + const char *port = ctx->argv[1]; > + const char *mirror_name = ctx->argv[2]; > + const struct nbrec_logical_switch_port *lsp = NULL; > + const struct nbrec_mirror *mirror; > + > + char *error; > + > + error = lsp_by_name_or_uuid(ctx, port, true, &lsp); > + if (error) { > + ctx->error = error; > + return; > + } > + > + /*check if a mirror rule actually exists on that name or not*/ > + error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror); > + if (error) { > + ctx->error = error; > + return; > + } > + > + /* Check if same mirror rule already exists for the lsp */ > + for (size_t i = 0; i < lsp->n_mirror_rules; i++) { > + if (uuid_equals(&lsp->mirror_rules[i]->header_.uuid, > + &mirror->header_.uuid)) { > + bool may_exist = shash_find(&ctx->options, "--may-exist") != > NULL; > + if (!may_exist) { > + ctl_error(ctx, "mirror %s is already attached to the " > + "logical port %s.", > + lsp->mirror_rules[i]->name, ctx->argv[1]); > + return; > + } > + return; > + } > + } > + > + nbrec_logical_switch_port_update_mirror_rules_addvalue(lsp, mirror); > +} > + > +static void > +nbctl_lsp_detach_mirror(struct ctl_context *ctx) > +{ > + const char *port = ctx->argv[1]; > + const char *mirror_name = ctx->argv[2]; > + const struct nbrec_logical_switch_port *lsp = NULL; > + const struct nbrec_mirror *mirror; > + > + char *error; > + > + error = lsp_by_name_or_uuid(ctx, port, true, &lsp); > + if (error) { > + ctx->error = error; > + return; > + } > + > + > + /*check if a mirror rule actually exists on that name or not*/ > + error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror); > + if (error) { > + ctx->error = error; > + return; > + } > + > + nbrec_logical_switch_port_update_mirror_rules_delvalue(lsp, mirror); > +} > + > static void > nbctl_lsp_set_type(struct ctl_context *ctx) > { > @@ -7250,6 +7385,192 @@ cmd_ha_ch_grp_set_chassis_prio(struct ctl_context > *ctx) > nbrec_ha_chassis_set_priority(ha_chassis, priority); > } > > +static char * OVS_WARN_UNUSED_RESULT > +parse_mirror_filter(const char *arg, const char **selection_p) > +{ > + /* Validate selection. Only require the first letter. */ > + if (arg[0] == 't') { > + *selection_p = "to-lport"; > + } else if (arg[0] == 'f') { > + *selection_p = "from-lport"; > + } else { > + *selection_p = NULL; > + return xasprintf("%s: selection must be \"to-lport\" or " > + "\"from-lport\"", arg); > + } > + return NULL; > +} > + > +static char * OVS_WARN_UNUSED_RESULT > +parse_mirror_tunnel_type(const char *arg, const char **type_p) > +{ > + /* Validate type. Only require the first letter. */ > + if (arg[0] == 'g') { > + *type_p = "gre"; > + } else if (arg[0] == 'e') { > + *type_p = "erspan"; > + } else { > + *type_p = NULL; > + return xasprintf("%s: type must be \"gre\" or " > + "\"erspan\"", arg); > + } > + return NULL; > +} > + > +static void > +nbctl_pre_mirror_add(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type); > +} > + > +static void > +nbctl_mirror_add(struct ctl_context *ctx) > +{ > + const char *filter = NULL; > + const char *sink_ip = NULL; > + const char *type = NULL; > + const char *name = NULL; > + char *new_sink_ip = NULL; > + int64_t index; > + char *error = NULL; > + const struct nbrec_mirror *mirror_check = NULL; > + > + /* Mirror Name */ > + name = ctx->argv[1]; > + NBREC_MIRROR_FOR_EACH (mirror_check, ctx->idl) { > + if (!strcmp(mirror_check->name, name)) { > + ctl_error(ctx, "Mirror with %s name already exists.", > + name); > + return; > + } > + } > + > + /* Tunnel Type - GRE/ERSPAN */ > + error = parse_mirror_tunnel_type(ctx->argv[2], &type); > + if (error) { > + ctx->error = error; > + return; > + } > + > + /* tunnel index / GRE key / ERSPAN idx */ > + if (!str_to_long(ctx->argv[3], 10, (long int *) &index)) { > + ctl_error(ctx, "Invalid Index"); > + return; > + } > + > + /* Filter for mirroring */ > + error = parse_mirror_filter(ctx->argv[4], &filter); > + if (error) { > + ctx->error = error; > + return; > + } > + > + /* Destination / Sink details */ > + sink_ip = ctx->argv[5]; > + > + /* check if it is a valid ip */ > + new_sink_ip = normalize_ipv4_addr_str(sink_ip); > + if (!new_sink_ip) { > + new_sink_ip = normalize_ipv6_addr_str(sink_ip); > + } > + > + if (!new_sink_ip) { > + ctl_error(ctx, "Invalid sink ip: %s", sink_ip); > + return; > + } > + free(new_sink_ip); > + > + /* Create the mirror. */ > + struct nbrec_mirror *mirror = nbrec_mirror_insert(ctx->txn); > + nbrec_mirror_set_name(mirror, name); > + nbrec_mirror_set_index(mirror, index); > + nbrec_mirror_set_filter(mirror, filter); > + nbrec_mirror_set_type(mirror, type); > + nbrec_mirror_set_sink(mirror, sink_ip); > + > +} > + > +static void > +nbctl_pre_mirror_del(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > +} > + > +static void > +nbctl_mirror_del(struct ctl_context *ctx) > +{ > + const struct nbrec_mirror *mirror, *next; > + > + /* If a name is not specified, delete all mirrors. */ > + if (ctx->argc == 1) { > + NBREC_MIRROR_FOR_EACH_SAFE (mirror, next, ctx->idl) { > + nbrec_mirror_delete(mirror); > + } > + return; > + } > + > + /* Remove the matching mirror. */ > + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { > + if (strcmp(ctx->argv[1], mirror->name)) { > + continue; > + } > + nbrec_mirror_delete(mirror); > + return; > + } > +} > + > +static void > +nbctl_pre_mirror_list(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink); > + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type); > +} > + > +static void > +nbctl_mirror_list(struct ctl_context *ctx) > +{ > + > + const struct nbrec_mirror **mirrors = NULL; > + const struct nbrec_mirror *mirror; > + size_t n_capacity = 0; > + size_t n_mirrors = 0; > + > + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { > + if (n_mirrors == n_capacity) { > + mirrors = x2nrealloc(mirrors, &n_capacity, sizeof *mirrors); > + } > + > + mirrors[n_mirrors] = mirror; > + n_mirrors++; > + } > + > + if (n_mirrors) { > + qsort(mirrors, n_mirrors, sizeof *mirrors, mirror_cmp); > + } > + > + for (size_t i = 0; i < n_mirrors; i++) { > + mirror = mirrors[i]; > + ds_put_format(&ctx->output, "%s:\n", mirror->name); > + /* print all the values */ > + ds_put_format(&ctx->output, " Type : %s\n", mirror->type); > + ds_put_format(&ctx->output, " Sink : %s\n", mirror->sink); > + ds_put_format(&ctx->output, " Filter : %s\n", mirror->filter); > + ds_put_format(&ctx->output, " Index/Key: %ld\n", > + (long int) mirror->index); > + ds_put_cstr(&ctx->output, "\n"); > + } > + > + free(mirrors); > +} > + > static const struct ctl_table_class tables[NBREC_N_TABLES] = { > [NBREC_TABLE_DHCP_OPTIONS].row_ids > = {{&nbrec_logical_switch_port_col_name, NULL, > @@ -7346,6 +7667,15 @@ static const struct ctl_command_syntax > nbctl_commands[] = { > { "qos-list", 1, 1, "SWITCH", nbctl_pre_qos_list, nbctl_qos_list, > NULL, "", RO }, > > + /* mirror commands. */ > + { "mirror-add", 5, 5, > + "NAME TYPE INDEX FILTER IP", > + nbctl_pre_mirror_add, nbctl_mirror_add, NULL, "--may-exist", RW }, > + { "mirror-del", 0, 1, "[NAME]", > + nbctl_pre_mirror_del, nbctl_mirror_del, NULL, "", RW }, > + { "mirror-list", 0, 0, "", nbctl_pre_mirror_list, nbctl_mirror_list, > + NULL, "", RO }, > + > /* meter commands. */ > { "meter-add", 4, 5, "NAME ACTION RATE UNIT [BURST]", > nbctl_pre_meter_add, > nbctl_meter_add, NULL, "--fair,--may-exist", RW }, > @@ -7400,6 +7730,10 @@ static const struct ctl_command_syntax > nbctl_commands[] = { > nbctl_lsp_get_dhcpv6_options, NULL, "", RO }, > { "lsp-get-ls", 1, 1, "PORT", nbctl_pre_lsp_get_ls, nbctl_lsp_get_ls, > NULL, "", RO }, > + { "lsp-attach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, > + nbctl_lsp_attach_mirror, NULL, "--may-exist", RW }, > + { "lsp-detach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, > + nbctl_lsp_detach_mirror, NULL, "", RW }, > > /* forwarding group commands. */ > { "fwd-group-add", 4, INT_MAX, "SWITCH GROUP VIP VMAC PORT...", > -- > 2.31.1 >
_______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
