Hi Numan,
Thanks a lot)

On 30.04.2025 07:26, Numan Siddique wrote:
> On Thu, Apr 24, 2025 at 8:55 AM Rukomoinikova Aleksandra
> <ARukomoinikova@k2.cloud> wrote:
>> Hi Numan, Dumitru and Ales,
>>
>> I sent the acked-by version from Numan, when will it be possible to get
>> into upstream?
>>
>> Sorry for disturbing you)
>
> Thanks for v11.  I added a NEWS item entry for this in patch 3 and
> applied the whole series to main.
>
> Numan
>
>> On 22.04.2025 12:10, Alexandra Rukomoinikova wrote:
>>> Added new mirror port type which is the link between the source
>>> and the target port, its parent is the target port. VLAN tagging
>>> is not required for mirror ports — packets are transmitted without
>>> VLAN tag. Also since lport mirror works due to logical flows, we
>>> don't need to pass information about the mirror to ovs.
>>>
>>> Signed-off-by: Alexandra Rukomoinikova <arukomoinikova@k2.cloud>
>>> Signed-off-by: Vladislav Odintsov <vlodintsov@k2.cloud>
>>> Co-authored-by: Vladislav Odintsov <vlodintsov@k2.cloud>
>>> Tested-by: Ivan Burnin <iburnin@k2.cloud>
>>> Acked-by: Numan Siddique <num...@ovn.org>
>>> ---
>>> v10 --> v11: added acked-by, fixed Numan comments.
>>> ---
>>>    controller/binding.c  | 110 +++++++++++-----
>>>    controller/mirror.c   |   4 +
>>>    controller/physical.c |  28 ++--
>>>    lib/ovn-util.c        |   5 +
>>>    lib/ovn-util.h        |   1 +
>>>    tests/ovn.at          | 291 ++++++++++++++++++++++++++++++++++++++++++
>>>    6 files changed, 396 insertions(+), 43 deletions(-)
>>>
>>> diff --git a/controller/binding.c b/controller/binding.c
>>> index fdb0ad124..f7535051f 100644
>>> --- a/controller/binding.c
>>> +++ b/controller/binding.c
>>> @@ -1537,14 +1537,15 @@ is_binding_lport_this_chassis(struct binding_lport 
>>> *b_lport,
>>>                 || is_postponed_port(b_lport->pb->logical_port)));
>>>    }
>>>
>>> -/* Returns 'true' if the 'lbinding' has binding lports of type 
>>> LP_CONTAINER,
>>> - * 'false' otherwise. */
>>> +/* Returns 'true' if the 'lbinding' has binding lports of type
>>> + * LP_CONTAINER/LP_MIRROR, 'false' otherwise. */
>>>    static bool
>>>    is_lbinding_container_parent(struct local_binding *lbinding)
>>>    {
>>>        struct binding_lport *b_lport;
>>>        LIST_FOR_EACH (b_lport, list_node, &lbinding->binding_lports) {
>>> -        if (b_lport->type == LP_CONTAINER) {
>>> +        if (b_lport->type == LP_CONTAINER ||
>>> +            b_lport->type == LP_MIRROR) {
>>>                return true;
>>>            }
>>>        }
>>> @@ -1705,12 +1706,16 @@ consider_vif_lport(const struct sbrec_port_binding 
>>> *pb,
>>>
>>>    static bool
>>>    consider_container_lport(const struct sbrec_port_binding *pb,
>>> +                         enum en_lport_type type,
>>>                             struct binding_ctx_in *b_ctx_in,
>>>                             struct binding_ctx_out *b_ctx_out)
>>>    {
>>>        struct shash *local_bindings = &b_ctx_out->lbinding_data->bindings;
>>>        struct local_binding *parent_lbinding;
>>> -    parent_lbinding = local_binding_find(local_bindings, pb->parent_port);
>>> +    const char *binding_port_name = (type == LP_MIRROR) ? pb->mirror_port :
>>> +                                    pb->parent_port;
>>> +
>>> +    parent_lbinding = local_binding_find(local_bindings, 
>>> binding_port_name);
>>>
>>>        if (!parent_lbinding) {
>>>            /* There is no local_binding for parent port. Create it
>>> @@ -1725,7 +1730,7 @@ consider_container_lport(const struct 
>>> sbrec_port_binding *pb,
>>>             *     we want the these container ports also be claimed by the
>>>             *     chassis.
>>>             * */
>>> -        parent_lbinding = local_binding_create(pb->parent_port, NULL);
>>> +        parent_lbinding = local_binding_create(binding_port_name, NULL);
>>>            local_binding_add(local_bindings, parent_lbinding);
>>>        }
>>>
>>> @@ -1739,17 +1744,25 @@ consider_container_lport(const struct 
>>> sbrec_port_binding *pb,
>>>            remove_related_lport(b_lport->pb, b_ctx_out);
>>>        }
>>>
>>> -    struct binding_lport *container_b_lport =
>>> -        local_binding_add_lport(binding_lports, parent_lbinding, pb,
>>> -                                LP_CONTAINER);
>>> +    struct binding_lport *container_b_lport;
>>> +
>>> +    if (type == LP_MIRROR) {
>>> +        container_b_lport = local_binding_add_lport(binding_lports,
>>> +                                                    parent_lbinding,
>>> +                                                    pb, LP_MIRROR);
>>> +    } else {
>>> +        container_b_lport = local_binding_add_lport(binding_lports,
>>> +                                                    parent_lbinding,
>>> +                                                    pb, LP_CONTAINER);
>>> +    }
>>>
>>>        struct binding_lport *parent_b_lport =
>>> -        binding_lport_find(binding_lports, pb->parent_port);
>>> +        binding_lport_find(binding_lports, binding_port_name);
>>>
>>>        bool can_consider_c_lport = true;
>>>        if (!parent_b_lport || !parent_b_lport->pb) {
>>>            const struct sbrec_port_binding *parent_pb = 
>>> lport_lookup_by_name(
>>> -            b_ctx_in->sbrec_port_binding_by_name, pb->parent_port);
>>> +            b_ctx_in->sbrec_port_binding_by_name, binding_port_name);
>>>
>>>            if (parent_pb && get_lport_type(parent_pb) == LP_VIF) {
>>>                /* Its possible that the parent lport is not considered yet.
>>> @@ -1757,7 +1770,7 @@ consider_container_lport(const struct 
>>> sbrec_port_binding *pb,
>>>                consider_vif_lport(parent_pb, b_ctx_in, b_ctx_out,
>>>                                   parent_lbinding);
>>>                parent_b_lport = binding_lport_find(binding_lports,
>>> -                                                pb->parent_port);
>>> +                                                binding_port_name);
>>>            } else {
>>>                /* The parent lport doesn't exist.  Cannot consider the 
>>> container
>>>                 * lport for binding. */
>>> @@ -1784,7 +1797,8 @@ consider_container_lport(const struct 
>>> sbrec_port_binding *pb,
>>>        }
>>>
>>>        ovs_assert(parent_b_lport && parent_b_lport->pb);
>>> -    /* cannot bind to this chassis if the parent_port cannot be bounded. */
>>> +    /* cannot bind to this chassis if the parent_port/mirror_port
>>> +     * cannot be bounded. */
>>>        /* Do not bind neither if parent is postponed */
>>>
>>>        enum can_bind can_bind =
>>> @@ -2209,7 +2223,11 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct 
>>> binding_ctx_out *b_ctx_out)
>>>                break;
>>>
>>>            case LP_CONTAINER:
>>> -            consider_container_lport(pb, b_ctx_in, b_ctx_out);
>>> +            consider_container_lport(pb, LP_CONTAINER, b_ctx_in, 
>>> b_ctx_out);
>>> +            break;
>>> +
>>> +        case LP_MIRROR:
>>> +            consider_container_lport(pb, LP_MIRROR, b_ctx_in, b_ctx_out);
>>>                break;
>>>
>>>            case LP_VIRTUAL:
>>> @@ -2485,8 +2503,9 @@ consider_iface_claim(const struct ovsrec_interface 
>>> *iface_rec,
>>>        /* Update the child local_binding's iface (if any children) and try 
>>> to
>>>         *  claim the container lbindings. */
>>>        LIST_FOR_EACH (b_lport, list_node, &lbinding->binding_lports) {
>>> -        if (b_lport->type == LP_CONTAINER) {
>>> -            if (!consider_container_lport(b_lport->pb, b_ctx_in, 
>>> b_ctx_out)) {
>>> +        if (b_lport->type == LP_CONTAINER || b_lport->type == LP_MIRROR) {
>>> +            if (!consider_container_lport(b_lport->pb, b_lport->type,
>>> +                                          b_ctx_in, b_ctx_out)) {
>>>                    return false;
>>>                }
>>>            }
>>> @@ -2575,7 +2594,7 @@ consider_iface_release(const struct ovsrec_interface 
>>> *iface_rec,
>>>                remove_related_lport(b_lport->pb, b_ctx_out);
>>>            }
>>>
>>> -        /* Check if the lbinding has children of type PB_CONTAINER.
>>> +        /* Check if the lbinding has children of type 
>>> PB_CONTAINER/PB_MIRROR.
>>>             * If so, don't delete the local_binding. */
>>>            if (!is_lbinding_container_parent(lbinding)) {
>>>                local_binding_delete(lbinding, local_bindings, 
>>> binding_lports,
>>> @@ -2883,13 +2902,13 @@ handle_deleted_vif_lport(const struct 
>>> sbrec_port_binding *pb,
>>>            }
>>>        }
>>>
>>> -    /* If its a container lport, then delete its entry from local_lports
>>> -     * if present.
>>> +    /* If its a container or mirror lport, then delete its entry from
>>> +     * local_lports if present.
>>>         * Note: If a normal lport is deleted, we don't want to remove
>>>         * it from local_lports if there is a VIF entry.
>>>         * consider_iface_release() takes care of removing from the 
>>> local_lports
>>>         * when the interface change happens. */
>>> -    if (lport_type == LP_CONTAINER) {
>>> +    if (lport_type == LP_CONTAINER || lport_type == LP_MIRROR) {
>>>            remove_local_lports(pb->logical_port, b_ctx_out);
>>>        }
>>>
>>> @@ -2912,8 +2931,10 @@ handle_updated_vif_lport(const struct 
>>> sbrec_port_binding *pb,
>>>
>>>        if (lport_type == LP_VIRTUAL) {
>>>            handled = consider_virtual_lport(pb, b_ctx_in, b_ctx_out);
>>> -    } else if (lport_type == LP_CONTAINER) {
>>> -        handled = consider_container_lport(pb, b_ctx_in, b_ctx_out);
>>> +    } else if (lport_type == LP_CONTAINER ||
>>> +               lport_type == LP_MIRROR) {
>>> +        handled = consider_container_lport(pb, lport_type, b_ctx_in,
>>> +                                           b_ctx_out);
>>>        } else {
>>>            handled = consider_vif_lport(pb, b_ctx_in, b_ctx_out, NULL);
>>>        }
>>> @@ -2925,8 +2946,8 @@ handle_updated_vif_lport(const struct 
>>> sbrec_port_binding *pb,
>>>        bool now_claimed = (pb->chassis == b_ctx_in->chassis_rec);
>>>
>>>        if (lport_type == LP_VIRTUAL || lport_type == LP_CONTAINER ||
>>> -            (claimed == now_claimed &&
>>> -             !is_additional_chassis(pb, b_ctx_in->chassis_rec))) {
>>> +        lport_type == LP_MIRROR || (claimed == now_claimed &&
>>> +        !is_additional_chassis(pb, b_ctx_in->chassis_rec))) {
>>>            return true;
>>>        }
>>>
>>> @@ -2944,9 +2965,9 @@ handle_updated_vif_lport(const struct 
>>> sbrec_port_binding *pb,
>>>
>>>        struct binding_lport *b_lport;
>>>        LIST_FOR_EACH (b_lport, list_node, &lbinding->binding_lports) {
>>> -        if (b_lport->type == LP_CONTAINER) {
>>> -            handled = consider_container_lport(b_lport->pb, b_ctx_in,
>>> -                                               b_ctx_out);
>>> +        if (b_lport->type == LP_CONTAINER || b_lport->type == LP_MIRROR) {
>>> +            handled = consider_container_lport(b_lport->pb, b_lport->type,
>>> +                                               b_ctx_in, b_ctx_out);
>>>                if (!handled) {
>>>                    return false;
>>>                }
>>> @@ -3069,6 +3090,7 @@ handle_updated_port(struct binding_ctx_in *b_ctx_in,
>>>        switch (lport_type) {
>>>        case LP_VIF:
>>>        case LP_CONTAINER:
>>> +    case LP_MIRROR:
>>>        case LP_VIRTUAL:
>>>            /* If port binding type just changed, port might be a 
>>> "related_lport"
>>>             * while it should not. Remove it from that set. It will be added
>>> @@ -3184,6 +3206,8 @@ binding_handle_port_binding_changes(struct 
>>> binding_ctx_in *b_ctx_in,
>>>         */
>>>        struct shash deleted_container_pbs =
>>>            SHASH_INITIALIZER(&deleted_container_pbs);
>>> +    struct shash deleted_mirror_pbs =
>>> +        SHASH_INITIALIZER(&deleted_mirror_pbs);
>>>        struct shash deleted_virtual_pbs =
>>>            SHASH_INITIALIZER(&deleted_virtual_pbs);
>>>        struct shash deleted_vif_pbs =
>>> @@ -3225,6 +3249,8 @@ binding_handle_port_binding_changes(struct 
>>> binding_ctx_in *b_ctx_in,
>>>                shash_add(&deleted_vif_pbs, pb->logical_port, pb);
>>>            } else if (lport_type == LP_CONTAINER) {
>>>                shash_add(&deleted_container_pbs, pb->logical_port, pb);
>>> +        } else if (lport_type == LP_MIRROR) {
>>> +            shash_add(&deleted_mirror_pbs, pb->logical_port, pb);
>>>            } else if (lport_type == LP_VIRTUAL) {
>>>                shash_add(&deleted_virtual_pbs, pb->logical_port, pb);
>>>            } else if (lport_type == LP_LOCALPORT) {
>>> @@ -3246,6 +3272,15 @@ binding_handle_port_binding_changes(struct 
>>> binding_ctx_in *b_ctx_in,
>>>            }
>>>        }
>>>
>>> +    SHASH_FOR_EACH_SAFE (node, &deleted_mirror_pbs) {
>>> +        handled = handle_deleted_vif_lport(node->data, LP_MIRROR, b_ctx_in,
>>> +                                           b_ctx_out);
>>> +        shash_delete(&deleted_mirror_pbs, node);
>>> +        if (!handled) {
>>> +            goto delete_done;
>>> +        }
>>> +    }
>>> +
>>>        SHASH_FOR_EACH_SAFE (node, &deleted_virtual_pbs) {
>>>            handled = handle_deleted_vif_lport(node->data, LP_VIRTUAL, 
>>> b_ctx_in,
>>>                                               b_ctx_out);
>>> @@ -3277,6 +3312,7 @@ binding_handle_port_binding_changes(struct 
>>> binding_ctx_in *b_ctx_in,
>>>
>>>    delete_done:
>>>        shash_destroy(&deleted_container_pbs);
>>> +    shash_destroy(&deleted_mirror_pbs);
>>>        shash_destroy(&deleted_virtual_pbs);
>>>        shash_destroy(&deleted_vif_pbs);
>>>        shash_destroy(&deleted_localport_pbs);
>>> @@ -3542,14 +3578,16 @@ local_binding_handle_stale_binding_lports(struct 
>>> local_binding *lbinding,
>>>                binding_lport_delete(&b_ctx_out->lbinding_data->lports,
>>>                                     b_lport);
>>>                handled = consider_virtual_lport(pb, b_ctx_in, b_ctx_out);
>>> -        } else if (b_lport->type == LP_CONTAINER &&
>>> -                   pb_lport_type == LP_CONTAINER) {
>>> +        } else if ((b_lport->type == LP_CONTAINER &&
>>> +                   pb_lport_type == LP_CONTAINER) ||
>>> +                   (b_lport->type == LP_MIRROR &&
>>> +                   pb_lport_type == LP_MIRROR)) {
>>>                /* For container lport, binding_lport is preserved so that 
>>> when
>>>                 * the parent port is created, it can be considered.
>>>                 * consider_container_lport() creates the binding_lport for 
>>> the parent
>>>                 * port (with iface set to NULL). */
>>> -            handled = consider_container_lport(b_lport->pb, b_ctx_in,
>>> -                                               b_ctx_out);
>>> +            handled = consider_container_lport(b_lport->pb, b_lport->type,
>>> +                                               b_ctx_in, b_ctx_out);
>>>            } else {
>>>                /* This can happen when the lport type changes from one type
>>>                 * to another. Eg. from normal lport to external.  Release 
>>> the
>>> @@ -3762,9 +3800,9 @@ binding_lport_get_parent_pb(struct binding_lport 
>>> *b_lport)
>>>     * If the 'b_lport' type is LP_VIF, then its name and its lbinding->name
>>>     * should match.  Otherwise this should be cleaned up.
>>>     *
>>> - * If the 'b_lport' type is LP_CONTAINER, then its parent_port name should
>>> - * be the same as its lbinding's name.  Otherwise this should be
>>> - * cleaned up.
>>> + * If the 'b_lport' type is LP_CONTAINER or LP_MIRROR, then its parent_port
>>> + * name should be the same as its lbinding's name. Otherwise this should
>>> + * be cleaned up.
>>>     *
>>>     * If the 'b_lport' type is LP_VIRTUAL, then its virtual parent name
>>>     * should be the same as its lbinding's name.  Otherwise this
>>> @@ -3799,6 +3837,12 @@ binding_lport_check_and_cleanup(struct binding_lport 
>>> *b_lport,
>>>            }
>>>            break;
>>>
>>> +    case LP_MIRROR:
>>> +        if (strcmp(b_lport->pb->mirror_port, b_lport->lbinding->name)) {
>>> +            cleanup_blport = true;
>>> +        }
>>> +        break;
>>> +
>>>        case LP_VIRTUAL:
>>>            if (!b_lport->pb->virtual_parent ||
>>>                strcmp(b_lport->pb->virtual_parent, 
>>> b_lport->lbinding->name)) {
>>> diff --git a/controller/mirror.c b/controller/mirror.c
>>> index b557b96da..2f1c811a0 100644
>>> --- a/controller/mirror.c
>>> +++ b/controller/mirror.c
>>> @@ -121,6 +121,10 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
>>>        /* 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) {
>>> +        /* We don't need to add mirror to ovs if it is lport mirror. */
>>> +        if (!strcmp(sb_mirror->type, "lport")) {
>>> +            continue;
>>> +        }
>>>            struct ovn_mirror *m = ovn_mirror_create(sb_mirror->name);
>>>            m->sb_mirror = sb_mirror;
>>>            ovn_mirror_add(&ovn_mirrors, m);
>>> diff --git a/controller/physical.c b/controller/physical.c
>>> index b4f91b134..b26d12d3c 100644
>>> --- a/controller/physical.c
>>> +++ b/controller/physical.c
>>> @@ -1883,23 +1883,31 @@ consider_port_binding(const struct physical_ctx 
>>> *ctx,
>>>
>>>        int tag = 0;
>>>        bool nested_container = false;
>>> -    const struct sbrec_port_binding *parent_port = NULL;
>>> +    const struct sbrec_port_binding *binding_port = NULL;
>>>        ofp_port_t ofport;
>>> -    if (binding->parent_port && *binding->parent_port) {
>>> -        if (!binding->tag) {
>>> +    bool is_mirror = (type == LP_MIRROR) ? true : false;
>>> +    if ((binding->parent_port && *binding->parent_port) || is_mirror) {
>>> +        if (!binding->tag && !is_mirror) {
>>>                return;
>>>            }
>>> +
>>> +        const char *binding_port_name = is_mirror ? binding->mirror_port :
>>> +                                        binding->parent_port;
>>>            ofport = local_binding_get_lport_ofport(ctx->local_bindings,
>>> -                                                binding->parent_port);
>>> +                                                binding_port_name);
>>>            if (ofport) {
>>> -            tag = *binding->tag;
>>> +            if (!is_mirror) {
>>> +                tag = *binding->tag;
>>> +            }
>>>                nested_container = true;
>>> -            parent_port = lport_lookup_by_name(
>>> -                ctx->sbrec_port_binding_by_name, binding->parent_port);
>>>
>>> -            if (parent_port
>>> +            binding_port
>>> +                = lport_lookup_by_name(ctx->sbrec_port_binding_by_name,
>>> +                                       binding_port_name);
>>> +
>>> +            if (binding_port
>>>                    && !lport_can_bind_on_this_chassis(ctx->chassis,
>>> -                                                   parent_port)) {
>>> +                                                  binding_port)) {
>>>                    /* Even though there is an ofport for this container
>>>                     * parent port, it is requested on different chassis 
>>> ignore
>>>                     * this ofport.
>>> @@ -1963,7 +1971,7 @@ consider_port_binding(const struct physical_ctx *ctx,
>>>            struct zone_ids zone_ids = get_zone_ids(binding, ctx->ct_zones);
>>>            /* Pass the parent port binding if the port is a nested
>>>             * container. */
>>> -        put_local_common_flows(dp_key, binding, parent_port, &zone_ids,
>>> +        put_local_common_flows(dp_key, binding, binding_port, &zone_ids,
>>>                                   &ctx->debug, ofpacts_p, flow_table);
>>>
>>>            /* Table 0, Priority 150 and 100.
>>> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
>>> index e2682ead7..af17082a9 100644
>>> --- a/lib/ovn-util.c
>>> +++ b/lib/ovn-util.c
>>> @@ -1206,6 +1206,8 @@ get_lport_type(const struct sbrec_port_binding *pb)
>>>            return LP_REMOTE;
>>>        } else if (!strcmp(pb->type, "vtep")) {
>>>            return LP_VTEP;
>>> +    } else if (!strcmp(pb->type, "mirror")) {
>>> +        return LP_MIRROR;
>>>        }
>>>
>>>        return LP_UNKNOWN;
>>> @@ -1219,6 +1221,8 @@ get_lport_type_str(enum en_lport_type lport_type)
>>>            return "VIF";
>>>        case LP_CONTAINER:
>>>            return "CONTAINER";
>>> +    case LP_MIRROR:
>>> +        return "MIRROR";
>>>        case LP_VIRTUAL:
>>>            return "VIRTUAL";
>>>        case LP_PATCH:
>>> @@ -1261,6 +1265,7 @@ is_pb_router_type(const struct sbrec_port_binding *pb)
>>>
>>>        case LP_VIF:
>>>        case LP_CONTAINER:
>>> +    case LP_MIRROR:
>>>        case LP_VIRTUAL:
>>>        case LP_LOCALNET:
>>>        case LP_LOCALPORT:
>>> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
>>> index 3c04ff221..36f8adc9b 100644
>>> --- a/lib/ovn-util.h
>>> +++ b/lib/ovn-util.h
>>> @@ -409,6 +409,7 @@ enum en_lport_type {
>>>        LP_UNKNOWN,
>>>        LP_VIF,
>>>        LP_CONTAINER,
>>> +    LP_MIRROR,
>>>        LP_PATCH,
>>>        LP_L3GATEWAY,
>>>        LP_LOCALNET,
>>> diff --git a/tests/ovn.at b/tests/ovn.at
>>> index 333068b99..52dd5bbe6 100644
>>> --- a/tests/ovn.at
>>> +++ b/tests/ovn.at
>>> @@ -18772,6 +18772,297 @@ OVN_CLEANUP([hv1],[hv2])
>>>    AT_CLEANUP
>>>    ])
>>>
>>> +OVN_FOR_EACH_NORTHD([
>>> +AT_SETUP([Mirror - lport: 2 HVs, 2 LS, 1 lport/LS, 2 peer LRs])
>>> +ovn_start
>>> +
>>> +# logical network:
>>> +# ls1 192.168.1.0/24 - lr1 - ls2 172.16.1.0/24
>>> +# ls1_lp1 - 192.168.1.2
>>> +# ls2_lp1 - 172.16.1.2
>>> +# ls2_lp2 - 172.16.1.3
>>> +#
>>> +# test cases
>>> +# sending packet from ls1_lp1 to ls2_lp1
>>> +# after mirror setting on ls1_lp1 expect to see
>>> +# mirrored packet on ls2_lp2
>>> +
>>> +ls1_lp1_mac="f0:00:00:01:02:03"
>>> +ls2_lp2_mac="f0:00:00:01:02:05"
>>> +rp_ls1_mac="00:00:00:01:02:03"
>>> +rp_ls2_mac="00:00:00:01:02:04"
>>> +ls2_lp1_mac="f0:00:00:01:02:04"
>>> +
>>> +ls1_lp1_ip="192.168.1.2"
>>> +ls2_lp1_ip="172.16.1.2"
>>> +ls2_lp2_ip="172.16.1.3"
>>> +
>>> +check ovn-nbctl lr-add R1
>>> +check ovn-nbctl lr-add R2
>>> +
>>> +check ovn-nbctl ls-add ls1
>>> +check ovn-nbctl ls-add ls2
>>> +
>>> +# Connect ls1 to R1
>>> +check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24
>>> +
>>> +check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 
>>> type=router \
>>> +  options:router-port=ls1 addresses=\"$rp_ls1_mac\"
>>> +
>>> +# Connect ls2 to R2
>>> +check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24
>>> +
>>> +check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 
>>> type=router \
>>> +  options:router-port=ls2 addresses=\"$rp_ls2_mac\"
>>> +
>>> +# Connect R1 to R2
>>> +check ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 20.0.0.1/24 peer=R2_R1
>>> +check ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 20.0.0.2/24 peer=R1_R2
>>> +
>>> +# Add static routes
>>> +check ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
>>> +check ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1
>>> +
>>> +# Create logical port ls1-lp1 in ls1
>>> +check ovn-nbctl lsp-add ls1 ls1-lp1 \
>>> +-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip"
>>> +
>>> +# Create logical port ls2-lp1 in ls2
>>> +check ovn-nbctl lsp-add ls2 ls2-lp1 \
>>> +-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip"
>>> +
>>> +# Create logical port ls2-lp2 in ls2
>>> +check ovn-nbctl lsp-add ls2 ls2-lp2 \
>>> +-- lsp-set-addresses ls2-lp2 "$ls2_lp2_mac $ls2_lp2_ip"
>>> +
>>> +# Create two hypervisor and create OVS ports corresponding to logical 
>>> ports.
>>> +net_add n1
>>> +
>>> +sim_add hv1
>>> +as hv1
>>> +ovs-vsctl add-br br-phys
>>> +ovn_attach n1 br-phys 192.168.0.5
>>> +ovs-vsctl -- add-port br-int hv1-vif1 -- \
>>> +    set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
>>> +    options:tx_pcap=hv1/vif1-tx.pcap \
>>> +    options:rxq_pcap=hv1/vif1-rx.pcap \
>>> +    ofport-request=1
>>> +
>>> +sim_add hv2
>>> +as hv2
>>> +ovs-vsctl add-br br-phys
>>> +ovn_attach n1 br-phys 192.168.0.6
>>> +ovs-vsctl -- add-port br-int hv2-vif1 -- \
>>> +    set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \
>>> +    options:tx_pcap=hv2/vif1-tx.pcap \
>>> +    options:rxq_pcap=hv2/vif1-rx.pcap \
>>> +    ofport-request=2
>>> +
>>> +sim_add hv3
>>> +as hv3
>>> +ovs-vsctl add-br br-phys
>>> +ovn_attach n1 br-phys 192.168.0.7
>>> +ovs-vsctl -- add-port br-int hv3-vif1 -- \
>>> +    set interface hv3-vif1 external-ids:iface-id=ls2-lp2 \
>>> +    options:tx_pcap=hv3/vif1-tx.pcap \
>>> +    options:rxq_pcap=hv3/vif1-rx.pcap \
>>> +    ofport-request=3
>>> +
>>> +
>>> +# Pre-populate the hypervisors' ARP tables.
>>> +OVN_POPULATE_ARP
>>> +
>>> +# Allow some time for ovn-northd and ovn-controller to catch up.
>>> +wait_for_ports_up
>>> +check ovn-nbctl --wait=hv sync
>>> +
>>> +# Packet to send.
>>> +packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && 
>>> eth.dst==$rp_ls1_mac &&
>>> +        ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4369"
>>> +OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
>>> +
>>> +expected="eth.src==$rp_ls2_mac && eth.dst==$ls2_lp1_mac &&
>>> +        ip4 && ip.ttl==62 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4369"
>>> +echo $expected | ovstest test-ovn expr-to-packets > expected
>>> +
>>> +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
>>> +
>>> +# Expect no packets on target port at the moment.
>>> +: > expected
>>> +OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [expected])
>>> +
>>> +check ovn-nbctl mirror-add mirror0 lport both ls2-lp2
>>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
>>> +
>>> +check ovn-nbctl --wait=hv sync
>>> +
>>> +check_row_count Port_Binding 1 logical_port=mp-ls1-ls2-lp2
>>> +
>>> +hv3_uuid=$(fetch_column sb:Chassis _uuid name=hv3)
>>> +check_column "$hv3_uuid" sb:Port_Binding chassis 
>>> logical_port=mp-ls1-ls2-lp2
>>> +
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep mirror| ovn_strip_lflows], [0], 
>>> [dnl
>>> +  table=??(ls_in_mirror       ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_in_mirror       ), priority=100  , match=(inport == 
>>> "ls1-lp1"), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +  table=??(ls_out_mirror      ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=100  , match=(outport == 
>>> "ls1-lp1"), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +])
>>> +
>>> +as hv2 reset_pcap_file hv2-vif1 hv2/vif1
>>> +
>>> +echo "---------------------"
>>> +ovn-sbctl show
>>> +
>>> +echo "---------------------"
>>> +ovn-sbctl list port_b
>>> +
>>> +hv3_uuid=`ovn-sbctl --bare --columns _uuid find Chassis name="hv3"`
>>> +check_column "$hv3_uuid" sb:Port_Binding chassis 
>>> logical_port=mp-ls1-ls2-lp2
>>> +
>>> +# Packet to send.
>>> +packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && 
>>> eth.dst==$rp_ls1_mac &&
>>> +        ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4369"
>>> +OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
>>> +
>>> +echo $packet | ovstest test-ovn expr-to-packets > packet
>>> +
>>> +expected="eth.src==$rp_ls2_mac && eth.dst==$ls2_lp1_mac &&
>>> +         ip4 && ip.ttl==62 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +         udp && udp.src==53 && udp.dst==4369"
>>> +echo $expected | ovstest test-ovn expr-to-packets > expected
>>> +
>>> +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
>>> +
>>> +# Expect mirrored packet on target port.
>>> +OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [packet])
>>> +
>>> +as hv2 reset_pcap_file hv2-vif1 hv2/vif1
>>> +as hv3 reset_pcap_file hv3-vif1 hv3/vif1
>>> +
>>> +# Expect no mirrored packet on target port after detaching.
>>> +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0
>>> +
>>> +check ovn-nbctl --wait=hv sync
>>> +
>>> +echo "---------------------"
>>> +ovn-sbctl show
>>> +
>>> +echo "---------------------"
>>> +ovn-sbctl list port_b
>>> +
>>> +check_row_count Port_Binding 0 logical_port=mp-ls1-ls2-lp2
>>> +
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep mirror| ovn_strip_lflows], [0], 
>>> [dnl
>>> +  table=??(ls_in_mirror       ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=0    , match=(1), action=(next;)
>>> +])
>>> +
>>> +as hv3 reset_pcap_file hv3-vif1 hv3/vif1
>>> +
>>> +# Packet to send.
>>> +packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && 
>>> eth.dst==$rp_ls1_mac &&
>>> +        ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4369"
>>> +OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
>>> +
>>> +expected="eth.src==$rp_ls2_mac && eth.dst==$ls2_lp1_mac &&
>>> +         ip4 && ip.ttl==62 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +         udp && udp.src==53 && udp.dst==4369"
>>> +echo $expected | ovstest test-ovn expr-to-packets > expected
>>> +
>>> +OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
>>> +
>>> +# Expect no packets on target port after detaching mirror.
>>> +: > expected
>>> +OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [expected])
>>> +
>>> +as hv2 reset_pcap_file hv2-vif1 hv2/vif1
>>> +as hv3 reset_pcap_file hv3-vif1 hv3/vif1
>>> +
>>> +# Test mirror filtering.
>>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
>>> +
>>> +check ovn-nbctl mirror-rule-add mirror0 200 '1' skip
>>> +check ovn-nbctl --wait=hv sync
>>> +
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep mirror | ovn_strip_lflows], [0], 
>>> [dnl
>>> +  table=??(ls_in_mirror       ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_in_mirror       ), priority=100  , match=(inport == 
>>> "ls1-lp1"), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +  table=??(ls_in_mirror       ), priority=300  , match=(inport == 
>>> "ls1-lp1" && (1)), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=100  , match=(outport == 
>>> "ls1-lp1"), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +  table=??(ls_out_mirror      ), priority=300  , match=(outport == 
>>> "ls1-lp1" && (1)), action=(next;)
>>> +])
>>> +
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep ls_out_pre_acl | 
>>> ovn_strip_lflows], [0], [dnl
>>> +  table=??(ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_pre_acl     ), priority=110  , match=(eth.src == 
>>> $svc_monitor_mac), action=(next;)
>>> +  table=??(ls_out_pre_acl     ), priority=65535, match=(outport == 
>>> "mp-ls1-ls2-lp2"), action=(next(pipeline=egress, table=??);)
>>> +])
>>> +
>>> +# Check target port deletion.
>>> +check ovn-nbctl lsp-del ls2-lp2
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep mirror | ovn_strip_lflows], [0], 
>>> [dnl
>>> +  table=??(ls_in_mirror       ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=0    , match=(1), action=(next;)
>>> +])
>>> +
>>> +check ovn-nbctl lsp-add ls2 ls2-lp2
>>> +
>>> +# Packet to send.
>>> +packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && 
>>> eth.dst==$rp_ls1_mac &&
>>> +        ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4369"
>>> +OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
>>> +
>>> +# Expect no packets on target port after rule added.
>>> +: > expected
>>> +OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [expected])
>>> +
>>> +check ovn-nbctl mirror-rule-add mirror0 300 'udp.dst == 4369' mirror
>>> +check ovn-nbctl --wait=hv sync
>>> +
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep mirror| ovn_strip_lflows], [0], 
>>> [dnl
>>> +  table=??(ls_in_mirror       ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_in_mirror       ), priority=100  , match=(inport == 
>>> "ls1-lp1"), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +  table=??(ls_in_mirror       ), priority=300  , match=(inport == 
>>> "ls1-lp1" && (1)), action=(next;)
>>> +  table=??(ls_in_mirror       ), priority=400  , match=(inport == 
>>> "ls1-lp1" && (udp.dst == 4369)), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +  table=??(ls_out_mirror      ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=100  , match=(outport == 
>>> "ls1-lp1"), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +  table=??(ls_out_mirror      ), priority=300  , match=(outport == 
>>> "ls1-lp1" && (1)), action=(next;)
>>> +  table=??(ls_out_mirror      ), priority=400  , match=(outport == 
>>> "ls1-lp1" && (udp.dst == 4369)), action=(mirror("mp-ls1-ls2-lp2"); next;)
>>> +])
>>> +
>>> +AT_CHECK([ovn-sbctl lflow-list ls1 | grep ls_out_pre_acl | 
>>> ovn_strip_lflows], [0], [dnl
>>> +  table=??(ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
>>> +  table=??(ls_out_pre_acl     ), priority=110  , match=(eth.src == 
>>> $svc_monitor_mac), action=(next;)
>>> +  table=??(ls_out_pre_acl     ), priority=65535, match=(outport == 
>>> "mp-ls1-ls2-lp2"), action=(next(pipeline=egress, table=??);)
>>> +])
>>> +
>>> +# Packet to send.
>>> +packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && 
>>> eth.dst==$rp_ls1_mac &&
>>> +        ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4369"
>>> +OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
>>> +
>>> +echo $packet | ovstest test-ovn expr-to-packets > packet
>>> +
>>> +packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && 
>>> eth.dst==$rp_ls1_mac &&
>>> +        ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip 
>>> &&
>>> +        udp && udp.src==53 && udp.dst==4368"
>>> +OVS_WAIT_UNTIL([as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"])
>>> +
>>> +OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [packet])
>>> +
>>> +check ovn-nbctl mirror-del
>>> +
>>> +OVN_CLEANUP([hv1],[hv2],[hv3])
>>> +AT_CLEANUP
>>> +])
>>> +
>>>    OVN_FOR_EACH_NORTHD([
>>>    AT_SETUP([Port Groups])
>>>    AT_KEYWORDS([ovnpg])
>>
>> --
>> regards,
>> Alexandra.
>>

-- 
regards,
Alexandra.

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to