Taas was designed to provide tenants and service providers a means of monitoring the traffic flowing in their Neutron provisioned virtual networks. It is useful for network trouble-shooting, security and analytics. The taas presentations could be found from https://github.com/openstack/tap-as-a-service/blob/master/doc/source/presentations.rst , and the api reference could be found from https://github.com/openstack/tap-as-a-service/blob/master/API_REFERENCE.rst
To support taas function, this patch add two type of logica_switch_port, "mirror" and "taas". port with type "mirror" is used as inport for monitor flow in logica_switch, and port with type "taas" is used as outport for monitor flow in logica_switch. The ovn-controller will make the relations of the ports in tap_service and tap_flow to mirror port and taas port. Signed-off-by: wang qianyu <wang.qia...@zte.com.cn> --- ovn/controller/binding.c | 12 ++ ovn/controller/ovn-controller.c | 2 + ovn/controller/physical.c | 185 +++++++++++++++++++++- ovn/lib/logical-fields.c | 4 + ovn/lib/logical-fields.h | 4 + ovn/northd/ovn-northd.c | 329 ++++++++++++++++++++++++++++++++++++++++ ovn/ovn-nb.xml | 69 +++++++++ 7 files changed, 603 insertions(+), 2 deletions(-) diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c index 32309e9..fc74ea0 100644 --- a/ovn/controller/binding.c +++ b/ovn/controller/binding.c @@ -437,6 +437,18 @@ consider_local_datapath(struct controller_ctx *ctx, * for them. */ sset_add(local_lports, binding_rec->logical_port); our_chassis = false; + } else if (!strcmp(binding_rec->type, "mirror")) { + add_local_datapath(ctx, binding_rec->datapath, + false, local_datapaths); + } else if (!strcmp(binding_rec->type, "taas")) { + const char *target_port_name = smap_get(&binding_rec->options, + "target-port"); + if (target_port_name && + sset_contains(local_lports, target_port_name)) { + our_chassis = true; + } + add_local_datapath(ctx, binding_rec->datapath, + false, local_datapaths); } if (ctx->ovnsb_idl_txn) { diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index e2c9652..0a148e4 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -150,6 +150,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl, struct ovsdb_idl_condition mg = OVSDB_IDL_CONDITION_INIT(&mg); struct ovsdb_idl_condition dns = OVSDB_IDL_CONDITION_INIT(&dns); sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "patch"); + sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "mirror"); + sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "taas"); /* XXX: We can optimize this, if we find a way to only monitor * ports that have a Gateway_Chassis that point's to our own * chassis */ diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c index df71979..7b55b04 100644 --- a/ovn/controller/physical.c +++ b/ovn/controller/physical.c @@ -291,9 +291,100 @@ load_logical_ingress_metadata(const struct sbrec_port_binding *binding, } static void +taas_port_handle(struct controller_ctx *ctx, + const struct sbrec_port_binding *binding, + struct ofpbuf *ofpacts_p, + struct hmap *flow_table, + uint32_t dp_key, + uint32_t port_key) +{ + const char *target_port_name = smap_get(&binding->options, + "target-port"); + if (!target_port_name) { + VLOG_INFO("taas port %s not configure target-port", + binding->logical_port); + return; + } + const struct sbrec_port_binding *target_port = lport_lookup_by_name( + ctx->ovnsb_idl, target_port_name); + if (!target_port) { + VLOG_INFO("can not find target port %s in this switch", + target_port_name); + return; + } + + ofp_port_t ofport = u16_to_ofp(simap_get(&localvif_to_ofport, + target_port_name)); + if (!ofport) { + VLOG_INFO("can not find ofport of %s in this switch", + target_port_name); + return; + } + struct match match; + + /* Table 33, priority 100. + * ======================= + * + * Implements output to local hypervisor. Each flow matches a + * logical output port on the local hypervisor, and resubmits to + * table 34. + */ + match_init_catchall(&match); + ofpbuf_clear(ofpacts_p); + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + + put_load(1, MFF_LOG_FLAGS, MLF_CLONED_FLOW_BIT, 1, ofpacts_p); + /* Resubmit to table 34. */ + put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p); + ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0, + &match, ofpacts_p); + + /* Table 65, Priority 100. + * ======================= + * + * Deliver the packet to the local vif. */ + match_init_catchall(&match); + ofpbuf_clear(ofpacts_p); + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + ofpact_put_OUTPUT(ofpacts_p)->port = ofport; + ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0, + &match, ofpacts_p); +} + +struct mirror_port { + struct sbrec_port_binding *port; + struct ovs_list list; +}; + +static void +get_mports_from_lport(struct shash *mports, + char *logical_port_name, + struct ovs_list *mport_list) +{ + struct shash_node *node, *next_node; + SHASH_FOR_EACH_SAFE (node, next_node, mports) { + struct sbrec_port_binding *binding = node->data; + const char *source_port_name = smap_get(&binding->options, + "source-port"); + if (!source_port_name) { + continue; + } + if (strcmp(source_port_name, logical_port_name)) { + continue; + } + struct mirror_port *mport = xzalloc(sizeof *mport); + mport->port = binding; + ovs_list_push_back(mport_list, &mport->list); + } +} + +static void consider_port_binding(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, const struct simap *ct_zones, + struct shash *mports, const struct chassis_index *chassis_index, struct sset *active_tunnels, struct hmap *local_datapaths, @@ -360,6 +451,13 @@ consider_port_binding(struct controller_ctx *ctx, return; } + if (!strcmp(binding->type, "taas") && + binding->chassis == chassis) { + taas_port_handle(ctx, binding, ofpacts_p, flow_table, + dp_key, port_key); + return; + } + struct ovs_list *gateway_chassis = gateway_chassis_get_ordered(binding, chassis_index); @@ -531,13 +629,45 @@ consider_port_binding(struct controller_ctx *ctx, ofpbuf_clear(ofpacts_p); match_init_catchall(&match); match_set_in_port(&match, ofport); + if (tag || !strcmp(binding->type, "localnet") + || !strcmp(binding->type, "l2gateway")) { + match_set_dl_vlan(&match, htons(tag)); + } + + struct ovs_list local_mports; + ovs_list_init(&local_mports); + get_mports_from_lport(mports, binding->logical_port, &local_mports); + struct mirror_port *mp, *next; + /* add mirror action of flow mirrored port in table 0 */ + LIST_FOR_EACH_SAFE (mp, next, list, &local_mports) { + const struct sbrec_port_binding *mirror_port = mp->port; + if (!mirror_port) { + continue; + } + const char *direction = smap_get(&mirror_port->options, + "direction"); + if (direction && (!strcmp(direction, "from-port") || + !strcmp(direction, "both"))) { + size_t clone_ofs = ofpacts_p->size; + struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts_p); + put_load(1, MFF_LOG_FLAGS, MLF_CLONED_FLOW_BIT, 1, ofpacts_p); + put_load(mirror_port->datapath->tunnel_key, MFF_LOG_DATAPATH, + 0, 64, ofpacts_p); + put_load(mirror_port->tunnel_key, MFF_LOG_INPORT, 0, 32, + ofpacts_p); + put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); + + clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof *clone); + ofpacts_p->header = clone; + ofpact_finish_CLONE(ofpacts_p, &clone); + } + } /* Match a VLAN tag and strip it, including stripping priority tags * (e.g. VLAN ID 0). In the latter case we'll add a second flow * for frames that lack any 802.1Q header later. */ if (tag || !strcmp(binding->type, "localnet") || !strcmp(binding->type, "l2gateway")) { - match_set_dl_vlan(&match, htons(tag)); if (nested_container) { /* When a packet comes from a container sitting behind a * parent_port, we should let it loopback to other containers @@ -586,6 +716,49 @@ consider_port_binding(struct controller_ctx *ctx, vlan_vid->push_vlan_if_needed = true; } ofpact_put_OUTPUT(ofpacts_p)->port = ofport; + + /* add mirror action of flow mirrored port in table 65 */ + LIST_FOR_EACH_SAFE (mp, next, list, &local_mports) { + const struct sbrec_port_binding *mirror_port = mp->port; + if (!mirror_port) { + continue; + } + const char *direction = smap_get(&mirror_port->options, + "direction"); + if (direction && (!strcmp(direction, "to-port") || + !strcmp(direction, "both"))) { + size_t clone_ofs = ofpacts_p->size; + struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts_p); + ofpact_put_CT_CLEAR(ofpacts_p); + put_load(0, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p); + put_load(0, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p); + put_load(0, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); + put_load(0, MFF_LOG_FLAGS, 0, 32, ofpacts_p); + put_load(0, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); + for (int i = 0; i < MFF_N_LOG_REGS; i++) { + put_load(0, MFF_LOG_REG0 + i, 0, 32, ofpacts_p); + } + + /* taas port may have the same chassis as the src port, + * so here need clear inport */ + put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); + put_load(1, MFF_LOG_FLAGS, MLF_CLONED_FLOW_BIT, 1, ofpacts_p); + put_load(mirror_port->datapath->tunnel_key, MFF_LOG_DATAPATH, + 0, 64, ofpacts_p); + put_load(mirror_port->tunnel_key, MFF_LOG_INPORT, + 0, 32, ofpacts_p); + put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); + + clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof *clone); + ofpacts_p->header = clone; + ofpact_finish_CLONE(ofpacts_p, &clone); + } + } + + LIST_FOR_EACH_SAFE (mp, next, list, &local_mports) { + free(mp); + } + if (tag) { /* Revert the tag added to the packets headed to containers * in the previous step. If we don't do this, the packets @@ -983,8 +1156,16 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, /* Set up flows in table 0 for physical-to-logical translation and in table * 64 for logical-to-physical translation. */ const struct sbrec_port_binding *binding; + + struct shash mports = SHASH_INITIALIZER(&mports); + SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { + if (!strcmp(binding->type, "mirror")) { + shash_add(&mports, binding->logical_port, binding); + } + } + SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { - consider_port_binding(ctx, mff_ovn_geneve, ct_zones, + consider_port_binding(ctx, mff_ovn_geneve, ct_zones, &mports, chassis_index, active_tunnels, local_datapaths, binding, chassis, &ofpacts, flow_table); diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c index 26e336f..511e896 100644 --- a/ovn/lib/logical-fields.c +++ b/ovn/lib/logical-fields.c @@ -105,6 +105,10 @@ ovn_init_symtab(struct shash *symtab) MLF_FORCE_SNAT_FOR_LB_BIT); expr_symtab_add_subfield(symtab, "flags.force_snat_for_lb", NULL, flags_str); + snprintf(flags_str, sizeof flags_str, "flags[%d]", + MLF_CLONED_FLOW_BIT); + expr_symtab_add_subfield(symtab, "flags.cloned_flow", NULL, + flags_str); /* Connection tracking state. */ expr_symtab_add_field(symtab, "ct_mark", MFF_CT_MARK, NULL, false); diff --git a/ovn/lib/logical-fields.h b/ovn/lib/logical-fields.h index 696c529..5e20608 100644 --- a/ovn/lib/logical-fields.h +++ b/ovn/lib/logical-fields.h @@ -49,6 +49,7 @@ enum mff_log_flags_bits { MLF_RCV_FROM_VXLAN_BIT = 1, MLF_FORCE_SNAT_FOR_DNAT_BIT = 2, MLF_FORCE_SNAT_FOR_LB_BIT = 3, + MLF_CLONED_FLOW_BIT = 4, }; /* MFF_LOG_FLAGS_REG flag assignments */ @@ -69,6 +70,9 @@ enum mff_log_flags { /* Indicate that a packet needs a force SNAT in the gateway router when * load-balancing has taken place. */ MLF_FORCE_SNAT_FOR_LB = (1 << MLF_FORCE_SNAT_FOR_LB_BIT), + + /* Indicate that a packet is cloned. */ + MLF_CLONED_FLOW = (1 << MLF_CLONED_FLOW_BIT), }; #endif /* ovn/lib/logical-fields.h */ diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 49e4ac3..59c9ba2 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -172,6 +172,8 @@ enum ovn_stage { * logical router dropping packets with source IP address equals * one of the logical router's own IP addresses. */ #define REGBIT_EGRESS_LOOPBACK "reg9[1]" +/* Indicate that a packet is cloned. */ +#define REGBIT_CLONED_FLOW "reg10[4]" /* Returns an "enum ovn_stage" built from the arguments. */ static enum ovn_stage @@ -3478,6 +3480,332 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows) } static void +build_mirror_flows(struct ovn_datapath *od, + struct hmap *ports, + struct hmap *lflows) +{ + struct ds match = DS_EMPTY_INITIALIZER; + struct ds actions = DS_EMPTY_INITIALIZER; + if (!od->nbs) { + return; + } + bool need_mirror = false; + for (size_t i = 0; i < od->nbs->n_ports; i++) { + const struct nbrec_logical_switch_port *nbsp + = od->nbs->ports[i]; + if (strcmp(nbsp->type, "mirror")) { + continue; + } + struct ovn_port *op = ovn_port_find(ports, nbsp->name); + if (!op) { + continue; + } + + /* Logical switch ingress table 15: L2_LKUP. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + /* build mirror flows */ + const char *taas_port_name = + smap_get( ->options, "taas-port"); + if (!taas_port_name) { + continue; + } + struct ovn_port *taas_port = + ovn_port_find(ports, taas_port_name); + if (!taas_port) { + continue; + } + if (taas_port->od != od) { + VLOG_INFO(" in valid configuration, inport: %s and outport %s is " + "not in same logical switch", nbsp->name, taas_port_name); + continue; + } + need_mirror = true; + + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_format(&actions, "outport = %s; output;", taas_port->json_key); + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 0: PORT_SEC_L2. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 1: PORT_SEC_IP. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 2: PORT_SEC_ND. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 3: PRE_ACL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 4: PRE_LB. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 5: PRE_STATEFUL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 6: ACL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 7: QOS_MARK. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_QOS_MARK, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 8: LB. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 9: STATEFUL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 10: ARP_ND_RSP. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 11: DHCP_OPTIONS. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 12: DHCP_RESPONSE. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 13: DNS_LOOKUP. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch ingress table 14: DNS_RESPONSE. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "inport == %s", op->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 0: PRE_LB. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 1: PRE_ACL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 2: PRE_STATEFUL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 3: LB. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 4: ACL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 5: QOS_MARK. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_QOS_MARK, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 6: STATEFUL. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 7: PORT_SEC_IP. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 65535, + ds_cstr(&match), ds_cstr(&actions)); + + /* Logical switch egress table 8: PORT_SEC_L2. (priority 65535) */ + ds_clear(&match); + ds_clear(&actions); + ds_put_format(&match, "outport == %s", taas_port->json_key); + ds_put_cstr(&match, " && flags.cloned_flow == 1"); + ds_put_cstr(&actions, "output;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 65535, + ds_cstr(&match), ds_cstr(&actions)); + } + if (need_mirror) { + /* drop all cloned packets to avoid the effect of normal flows */ + /* ingress pipeline */ + ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_QOS_MARK, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 65534, + "flags.cloned_flow == 1", "drop;"); + + /* egress pipeline */ + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_QOS_MARK, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 65534, + "flags.cloned_flow == 1", "drop;"); + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 65534, + "flags.cloned_flow == 1", "drop;"); + } + ds_destroy(&match); + ds_destroy(&actions); +} +static void build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, struct hmap *lflows, struct hmap *mcgroups) { @@ -3502,6 +3830,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, build_qos(od, lflows); build_lb(od, lflows); build_stateful(od, lflows); + build_mirror_flows(od, ports, lflows); } /* Logical switch ingress table 0: Admission control framework (priority diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 31303a8..5fdd045 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -301,6 +301,20 @@ <dd> A port to a logical switch on a VTEP gateway. </dd> + + <dt><code>mirror</code></dt> + <dd> + A port indicate the inport of mirrored flows. The user need to + create this port in the logical_switch. This port should one to + one correspondence with the the tap_flows + </dd> + + <dt><code>taas</code></dt> + <dd> + A port indicate the outport of mirrored flows. The user need to + create this port in logical_switch. This port should one to + one correspondence with the the tap_service. + </dd> </dl> </column> </group> @@ -445,6 +459,61 @@ interface, in bits. </column> </group> + + <group title="Options for mirror ports"> + <p> + These options apply when <ref column="type"/> is + <code>mirror</code>. + </p> + + <column name="options" key="source-port"> + Required. The <ref column="name"/> of the <ref + table="Logical_switch_Port"/> that indicates where the + cloned flows come from. + </column> + + <column name="options" key="taas-port"> + Required. The <ref column="name"/> of the <ref + table="Logical_switch_Port"/> with type taas. + </column> + + <column name="options" key="direction"> + <p> + This option indicates whitch direction(from-port/to-port/all) of + packet will be cloned to the taas-port. The directions are defined + as follow: + </p> + <dl> + <dt><code>from-port</code></dt> + <dd> + The packets from this port will be cloned to specified mirror + port. + </dd> + <dt><code>to-port</code></dt> + <dd> + The packets to this port will be cloned to specified mirror + port. + </dd> + <dt><code>both</code></dt> + <dd> + The packets both from and to this port will be cloned to + specified mirror port. + </dd> + </dl> + </column> + </group> + + <group title="Options for taas ports"> + <p> + These options apply when <ref column="type"/> is <code>taas</code>. + </p> + + <column name="options" key="target-port"> + Required. The <ref column="name"/> of the <ref + table="Logical_switch_Port"/> that indicates where the + cloned flows come to. + </column> + </group> </group> <group title="Containers"> -- 1.8.3.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev