Skip to content

Commit

Permalink
treewide: Introduce OVN overlay port mirroring support.
Browse files Browse the repository at this point in the history
Today the mirror feature in OVN supports only tunnel to local
and remote to ports that locate outside of OVN claster.

With this feature, traffic to/from a virtual port that
can be mirrored to dedicated OVN port.

To enable overlay port mirroring with filter functions,
we have introduced the necessary schemas in the Northbound db
and the associated XML configuration:
1. A field for mirror rules has been added to the mirror table
to add reflection rules of mirrored traffic.
2. A new table, titled "Mirror Rule," has been established
to filter overlay remote traffic.
3. A new mirror type, titled "lport", has been established
for encapsulate mirror traffic to another ovn port
4. A new port type, titles "mirror" has been established for
serve as a conductor to the target port.

For lport mirrors, all processing occurs within OVN, making it
unnecessary to involve OVS. In the case of lport mirror we add
logical flows about the necessary actions with the package.

A new stages named "MIRROR" in logical flow table has been
introduced, allowing specification of mirror rule filters for
the lport mirror type.

Added stage number 2 in the ingress pipeline of the logical switch
and table number 7 in the egress pipeline of the logical switch.

Packets that meet these criteria are duplicated and delivered to
the target port, while the original packet follows its designated
pipeline. Northbound's mirror rule table enables the creation of
these filters.

In case of creating a mirror with the lport type without any rules
attached to it, default logical flows are added that duplicate all
incoming/outgoing traffic to the target port.

At the time of attaching lport mirror to logical switch port, a new
port binding with name  mp-datapath-target port is created,
it's a mirror port with a parent that is the target port,
tagging is unnecessary, packets are sent without VLAN header
encapsulation.

Signed-off-by: Alexandra Rukomoinikova <[email protected]>
Signed-off-by: Vladislav Odintsov <[email protected]>
Co-authored-by: Vladislav Odintsov <[email protected]>
Tested-by: Ivan Burnin <[email protected]>
  • Loading branch information
Sashhkaa and odivlad committed Feb 6, 2025
1 parent 33ecea5 commit 3285e7d
Show file tree
Hide file tree
Showing 20 changed files with 1,361 additions and 141 deletions.
95 changes: 69 additions & 26 deletions controller/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -1525,14 +1525,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;
}
}
Expand Down Expand Up @@ -1692,7 +1693,11 @@ consider_container_lport(const struct sbrec_port_binding *pb,
{
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);
bool is_mirror = !strcmp(pb->type, "mirror");
const char *binding_port_name = is_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
Expand All @@ -1707,7 +1712,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);
}

Expand All @@ -1721,25 +1726,34 @@ 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;

struct binding_lport *parent_b_lport =
binding_lport_find(binding_lports, pb->parent_port);
if (is_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,
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.
* So call consider_vif_lport() to process it first. */
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. */
Expand All @@ -1766,7 +1780,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 */
bool can_bind = lport_can_bind_on_this_chassis(b_ctx_in->chassis_rec,
parent_b_lport->pb) &&
Expand Down Expand Up @@ -2182,6 +2197,10 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
consider_container_lport(pb, b_ctx_in, b_ctx_out);
break;

case LP_MIRROR:
consider_container_lport(pb, b_ctx_in, b_ctx_out);
break;

case LP_VIRTUAL:
consider_virtual_lport(pb, b_ctx_in, b_ctx_out);
break;
Expand Down Expand Up @@ -2453,7 +2472,7 @@ 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 (b_lport->type == LP_CONTAINER || b_lport->type == LP_MIRROR) {
if (!consider_container_lport(b_lport->pb, b_ctx_in, b_ctx_out)) {
return false;
}
Expand Down Expand Up @@ -2544,7 +2563,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,
Expand Down Expand Up @@ -2852,13 +2871,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);
}

Expand All @@ -2882,7 +2901,8 @@ 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) {
} else if (lport_type == LP_CONTAINER ||
lport_type == LP_MIRROR) {
handled = consider_container_lport(pb, b_ctx_in, b_ctx_out);
} else {
handled = consider_vif_lport(pb, b_ctx_in, b_ctx_out, NULL);
Expand All @@ -2895,7 +2915,7 @@ 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) {
lport_type == LP_MIRROR || claimed == now_claimed) {
return true;
}

Expand All @@ -2913,7 +2933,7 @@ 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) {
if (b_lport->type == LP_CONTAINER || b_lport->type == LP_MIRROR) {
handled = consider_container_lport(b_lport->pb, b_ctx_in,
b_ctx_out);
if (!handled) {
Expand Down Expand Up @@ -3023,6 +3043,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
Expand Down Expand Up @@ -3133,6 +3154,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 =
Expand Down Expand Up @@ -3174,6 +3197,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) {
Expand All @@ -3195,6 +3220,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);
Expand Down Expand Up @@ -3226,6 +3260,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);
Expand Down Expand Up @@ -3491,8 +3526,10 @@ 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
Expand Down Expand Up @@ -3710,9 +3747,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
Expand Down Expand Up @@ -3747,6 +3784,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)) {
Expand Down
12 changes: 6 additions & 6 deletions controller/lflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ struct uuid;

/* Start of LOG_PIPELINE_LEN tables. */
#define OFTABLE_LOG_INGRESS_PIPELINE 8
#define OFTABLE_OUTPUT_LARGE_PKT_DETECT 40
#define OFTABLE_OUTPUT_LARGE_PKT_PROCESS 41
#define OFTABLE_REMOTE_OUTPUT 42
#define OFTABLE_LOCAL_OUTPUT 43
#define OFTABLE_CHECK_LOOPBACK 44
#define OFTABLE_OUTPUT_LARGE_PKT_DETECT 41
#define OFTABLE_OUTPUT_LARGE_PKT_PROCESS 42
#define OFTABLE_REMOTE_OUTPUT 43
#define OFTABLE_LOCAL_OUTPUT 44
#define OFTABLE_CHECK_LOOPBACK 45

/* Start of the OUTPUT section of the pipeline. */
#define OFTABLE_OUTPUT_INIT OFTABLE_OUTPUT_LARGE_PKT_DETECT

/* Start of LOG_PIPELINE_LEN tables. */
#define OFTABLE_LOG_EGRESS_PIPELINE 45
#define OFTABLE_LOG_EGRESS_PIPELINE 46
#define OFTABLE_SAVE_INPORT 64
#define OFTABLE_LOG_TO_PHY 65
#define OFTABLE_MAC_BINDING 66
Expand Down
4 changes: 4 additions & 0 deletions controller/mirror.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 18 additions & 10 deletions controller/physical.c
Original file line number Diff line number Diff line change
Expand Up @@ -1758,23 +1758,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 = !strcmp(binding->type, "mirror");
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) != CAN_BIND_AS_MAIN)) {
binding_port) != CAN_BIND_AS_MAIN)) {
/* Even though there is an ofport for this container
* parent port, it is requested on different chassis ignore
* this container port.
Expand Down Expand Up @@ -1838,7 +1846,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.
Expand Down
12 changes: 12 additions & 0 deletions lib/ovn-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,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;
Expand All @@ -1209,6 +1211,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:
Expand Down Expand Up @@ -1251,6 +1255,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:
Expand Down Expand Up @@ -1367,3 +1372,10 @@ lport_lookup_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,

return retval;
}

char *
ovn_mirror_port_name(const char *datapath_name,
const char *port_name)
{
return xasprintf("mp-%s-%s", datapath_name, port_name);
}
Loading

0 comments on commit 3285e7d

Please sign in to comment.