From: RYAN D. MOATS <rmo...@us.ibm.com> This code changes to allow incremental processing of the logical flow and physical binding tables whenver possible.
Side Effects: - Make flow table persistent in ovn controller - Reset lflow processing when adding/removing patch ports Note: flows created by physical_run for multicast_groups are *NOT* handled incrementally due to to be solved issues with GWs and local routers. Signed-off-by: RYAN D. MOATS <rmo...@us.ibm.com> --- ovn/controller/binding.c | 7 +- ovn/controller/encaps.c | 4 +- ovn/controller/lflow.c | 177 ++++++--- ovn/controller/lflow.h | 3 + ovn/controller/lport.c | 12 +- ovn/controller/ovn-controller.c | 1 - ovn/controller/patch.c | 4 +- ovn/controller/physical.c | 896 +++++++++++++++++++++----------------- ovn/controller/physical.h | 2 + 9 files changed, 644 insertions(+), 462 deletions(-) diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c index 7f8c26c..87f4362 100644 --- a/ovn/controller/binding.c +++ b/ovn/controller/binding.c @@ -15,6 +15,8 @@ #include <config.h> #include "binding.h" +#include "lflow.h" +#include "lport.h" #include "lib/bitmap.h" #include "lib/hmap.h" @@ -163,7 +165,7 @@ remove_local_datapath(struct hmap *local_datapaths, const struct uuid *uuid) hmap_remove(local_datapaths, &ld->hmap_node); hmap_remove(&local_datapaths_by_uuid, &ld->uuid_hmap_node); free(ld); - //reset_flow_processing(); + reset_flow_processing(); } } @@ -183,7 +185,7 @@ add_local_datapath(struct hmap *local_datapaths, binding_rec->datapath->tunnel_key); hmap_insert(&local_datapaths_by_uuid, &ld->uuid_hmap_node, uuid_hash(uuid)); - //reset_flow_processing(); + reset_flow_processing(); } static void @@ -277,6 +279,7 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, consider_local_datapath(ctx, &lports, chassis_rec, binding_rec, local_datapaths); } + flag_rebuild_lport_mcast_indexes(); process_full_binding = false; } else { SBREC_PORT_BINDING_FOR_EACH_TRACKED(binding_rec, ctx->ovnsb_idl) { diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c index 271ddef..ffb8e46 100644 --- a/ovn/controller/encaps.c +++ b/ovn/controller/encaps.c @@ -217,7 +217,7 @@ tunnel_add(const struct sbrec_chassis *chassis_rec, sset_add(&tc.port_names, port_name); free(port_name); free(ports); - // reset_flow_processing(); + reset_flow_processing(); reset_binding_processing(); process_full_encaps = true; } @@ -341,7 +341,7 @@ encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, hmap_remove(&tc.tunnel_hmap_by_uuid, &port_hash->uuid_node); free(port_hash); - //reset_flow_processing(); + reset_flow_processing(); reset_binding_processing(); } continue; diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c index 7e49e6d..caa16e7 100644 --- a/ovn/controller/lflow.c +++ b/ovn/controller/lflow.c @@ -26,6 +26,7 @@ #include "ovn/lib/expr.h" #include "ovn/lib/ovn-sb-idl.h" #include "packets.h" +#include "physical.h" #include "simap.h" VLOG_DEFINE_THIS_MODULE(lflow); @@ -35,6 +36,16 @@ VLOG_DEFINE_THIS_MODULE(lflow); /* Contains "struct expr_symbol"s for fields supported by OVN lflows. */ static struct shash symtab; +static bool full_flow_processing = false; +static bool full_logical_flow_processing = false; +static bool full_neighbor_flow_processing = false; + +void +reset_flow_processing(void) +{ + full_flow_processing = true; +} + static void add_logical_register(struct shash *symtab, enum mf_field_id id) { @@ -193,24 +204,22 @@ is_switch(const struct sbrec_datapath_binding *ldp) } -/* Adds the logical flows from the Logical_Flow table to flow tables. */ static void -add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, - const struct mcgroup_index *mcgroups, - const struct hmap *local_datapaths, - const struct hmap *patched_datapaths, - const struct simap *ct_zones) +consider_logical_flow(const struct lport_index *lports, + const struct mcgroup_index *mcgroups, + const struct sbrec_logical_flow *lflow, + const struct hmap *local_datapaths, + const struct hmap *patched_datapaths, + const struct simap *ct_zones, + uint32_t *conj_id_ofs_p, + bool is_new) { - uint32_t conj_id_ofs = 1; - - const struct sbrec_logical_flow *lflow; - SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) { /* Determine translation of logical table IDs to physical table IDs. */ bool ingress = !strcmp(lflow->pipeline, "ingress"); const struct sbrec_datapath_binding *ldp = lflow->logical_datapath; if (!ldp) { - continue; + return; } if (is_switch(ldp)) { /* For a logical switch datapath, local_datapaths tells us if there @@ -243,11 +252,11 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) { if (!ingress) { - continue; + return; } if (!get_patched_datapath(patched_datapaths, ldp->tunnel_key)) { - continue; + return; } } } @@ -293,7 +302,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", lflow->actions, error); free(error); - continue; + return; } /* Translate OVN match into table of OpenFlow matches. */ @@ -315,7 +324,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, expr_destroy(prereqs); ofpbuf_uninit(&ofpacts); free(error); - continue; + return; } expr = expr_simplify(expr); @@ -330,11 +339,11 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, match_set_metadata(&m->match, htonll(lflow->logical_datapath->tunnel_key)); if (m->match.wc.masks.conj_id) { - m->match.flow.conj_id += conj_id_ofs; + m->match.flow.conj_id += *conj_id_ofs_p; } if (!m->n) { ofctrl_add_flow(ptable, lflow->priority, &m->match, &ofpacts, - &lflow->header_.uuid, true); + &lflow->header_.uuid, is_new); } else { uint64_t conj_stubs[64 / 8]; struct ofpbuf conj; @@ -345,20 +354,54 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, struct ofpact_conjunction *dst; dst = ofpact_put_CONJUNCTION(&conj); - dst->id = src->id + conj_id_ofs; + dst->id = src->id + *conj_id_ofs_p; dst->clause = src->clause; dst->n_clauses = src->n_clauses; } ofctrl_add_flow(ptable, lflow->priority, &m->match, &conj, - &lflow->header_.uuid, true); + &lflow->header_.uuid, is_new); ofpbuf_uninit(&conj); } } - /* Clean up. */ expr_matches_destroy(&matches); ofpbuf_uninit(&ofpacts); - conj_id_ofs += n_conjs; + *conj_id_ofs_p += n_conjs; +} + +/* Adds the logical flows from the Logical_Flow table to 'flow_table'. */ +static void +add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports, + const struct mcgroup_index *mcgroups, + const struct hmap *local_datapaths, + const struct hmap *patched_datapaths, + const struct simap *ct_zones) +{ + uint32_t conj_id_ofs = 1; + const struct sbrec_logical_flow *lflow; + + if (full_logical_flow_processing) { + SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) { + consider_logical_flow(lports, mcgroups, lflow, local_datapaths, + patched_datapaths, ct_zones, &conj_id_ofs, + true); + } + full_logical_flow_processing = false; + } else { + SBREC_LOGICAL_FLOW_FOR_EACH_TRACKED (lflow, ctx->ovnsb_idl) { + bool is_deleted = sbrec_logical_flow_row_get_seqno(lflow, + OVSDB_IDL_CHANGE_DELETE) > 0; + bool is_new = sbrec_logical_flow_row_get_seqno(lflow, + OVSDB_IDL_CHANGE_MODIFY) == 0; + + if (is_deleted) { + ofctrl_remove_flows(&lflow->header_.uuid); + continue; + } + consider_logical_flow(lports, mcgroups, lflow, local_datapaths, + patched_datapaths, ct_zones, &conj_id_ofs, + is_new); + } } } @@ -375,6 +418,46 @@ put_load(const uint8_t *data, size_t len, bitwise_one(&sf->mask, sf->field->n_bytes, ofs, n_bits); } +unsigned int lflow_mac_binding_seqno = 0; + +static void +consider_neighbor_flow(const struct lport_index *lports, + const struct sbrec_mac_binding *b, + struct ofpbuf *ofpacts_p, + struct match *match_p, + bool is_new) +{ + const struct sbrec_port_binding *pb + = lport_lookup_by_name(lports, b->logical_port); + if (!pb) { + return; + } + + struct eth_addr mac; + if (!eth_addr_from_string(b->mac, &mac)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac); + return; + } + + ovs_be32 ip; + if (!ip_parse(b->ip, &ip)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); + return; + } + + match_set_metadata(match_p, htonll(pb->datapath->tunnel_key)); + match_set_reg(match_p, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key); + match_set_reg(match_p, 0, ntohl(ip)); + + ofpbuf_clear(ofpacts_p); + put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, ofpacts_p); + + ofctrl_add_flow(OFTABLE_MAC_BINDING, 100, match_p, ofpacts_p, + &b->header_.uuid, is_new); +} + /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN * southbound database, using 'lports' to resolve logical port names to * numbers. */ @@ -388,36 +471,24 @@ add_neighbor_flows(struct controller_ctx *ctx, ofpbuf_init(&ofpacts, 0); const struct sbrec_mac_binding *b; - SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) { - const struct sbrec_port_binding *pb - = lport_lookup_by_name(lports, b->logical_port); - if (!pb) { - continue; - } - - struct eth_addr mac; - if (!eth_addr_from_string(b->mac, &mac)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac); - continue; + if (full_neighbor_flow_processing) { + SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) { + consider_neighbor_flow(lports, b, &ofpacts, &match, true); } - - ovs_be32 ip; - if (!ip_parse(b->ip, &ip)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); - continue; + full_neighbor_flow_processing = false; + } else { + SBREC_MAC_BINDING_FOR_EACH_TRACKED (b, ctx->ovnsb_idl) { + bool is_deleted = sbrec_mac_binding_row_get_seqno(b, + OVSDB_IDL_CHANGE_DELETE) > 0; + bool is_new = sbrec_mac_binding_row_get_seqno(b, + OVSDB_IDL_CHANGE_MODIFY) == 0; + + if (is_deleted) { + ofctrl_remove_flows(&b->header_.uuid); + continue; + } + consider_neighbor_flow(lports, b, &ofpacts, &match, is_new); } - - match_set_metadata(&match, htonll(pb->datapath->tunnel_key)); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key); - match_set_reg(&match, 0, ntohl(ip)); - - ofpbuf_clear(&ofpacts); - put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts); - - ofctrl_add_flow(OFTABLE_MAC_BINDING, 100, &match, &ofpacts, - &b->header_.uuid, true); } ofpbuf_uninit(&ofpacts); } @@ -431,6 +502,14 @@ lflow_run(struct controller_ctx *ctx, const struct lport_index *lports, const struct hmap *patched_datapaths, const struct simap *ct_zones) { + if (full_flow_processing) { + ovn_flow_table_clear(); + localvif_to_ofports_clear(); + tunnels_clear(); + full_logical_flow_processing = true; + full_neighbor_flow_processing = true; + full_flow_processing = false; + } add_logical_flows(ctx, lports, mcgroups, local_datapaths, patched_datapaths, ct_zones); add_neighbor_flows(ctx, lports); diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h index 8f8f81a..26cb5e0 100644 --- a/ovn/controller/lflow.h +++ b/ovn/controller/lflow.h @@ -65,5 +65,8 @@ void lflow_run(struct controller_ctx *, const struct lport_index *, const struct hmap *patched_datapaths, const struct simap *ct_zones); void lflow_destroy(void); +void reset_flow_processing(void); + +void reset_flow_processing(void); #endif /* ovn/lflow.h */ diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c index 8210bc9..0674212 100644 --- a/ovn/controller/lport.c +++ b/ovn/controller/lport.c @@ -75,7 +75,7 @@ lport_index_clear(struct lport_index *lports) hmap_remove(&lports->by_uuid, &port->uuid_node); free(port); } - //reset_flow_processing(); + reset_flow_processing(); } static void @@ -95,7 +95,7 @@ consider_lport_index(struct lport_index *lports, uuid_hash(&pb->header_.uuid)); p->uuid = &pb->header_.uuid; p->pb = pb; - //reset_flow_processing(); + reset_flow_processing(); } void @@ -115,7 +115,7 @@ lport_index_fill(struct lport_index *lports, struct ovsdb_idl *ovnsb_idl) if (is_delete) { lport_index_remove(lports, &pb->header_.uuid); - //reset_flow_processing(); + reset_flow_processing(); continue; } consider_lport_index(lports, pb); @@ -213,7 +213,7 @@ mcgroup_index_clear(struct mcgroup_index *mcgroups) hmap_remove(&mcgroups->by_uuid, &mcgroup->uuid_node); free(mcgroup); } - //reset_flow_processing(); + reset_flow_processing(); } static void @@ -232,7 +232,7 @@ consider_mcgroup_index(struct mcgroup_index *mcgroups, uuid_hash(&mg->header_.uuid)); m->uuid = &mg->header_.uuid; m->mg = mg; - //reset_flow_processing(); + reset_flow_processing(); } void @@ -252,7 +252,7 @@ mcgroup_index_fill(struct mcgroup_index *mcgroups, struct ovsdb_idl *ovnsb_idl) if (is_delete) { mcgroup_index_remove(mcgroups, &mg->header_.uuid); - //reset_flow_processing(); + reset_flow_processing(); continue; } consider_mcgroup_index(mcgroups, mg); diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 5eba924..602ab09 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -363,7 +363,6 @@ main(int argc, char *argv[]) pinctrl_run(&ctx, &lports, br_int); - ovn_flow_table_clear(); lflow_run(&ctx, &lports, &mcgroups, &local_datapaths, &patched_datapaths, &ct_zones); if (chassis_id) { diff --git a/ovn/controller/patch.c b/ovn/controller/patch.c index e07e829..2720f6c 100644 --- a/ovn/controller/patch.c +++ b/ovn/controller/patch.c @@ -15,6 +15,7 @@ #include <config.h> +#include "lflow.h" #include "patch.h" #include "hash.h" @@ -92,7 +93,7 @@ create_patch_port(struct controller_ctx *ctx, ports[src->n_ports] = port; ovsrec_bridge_verify_ports(src); ovsrec_bridge_set_ports(src, ports, src->n_ports + 1); - + reset_flow_processing(); free(ports); } @@ -125,6 +126,7 @@ remove_port(struct controller_ctx *ctx, return; } } + reset_flow_processing(); } /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c index 8e39e40..3242b3e 100644 --- a/ovn/controller/physical.c +++ b/ovn/controller/physical.c @@ -48,8 +48,12 @@ physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids); } -struct uuid *hc_uuid = NULL; // uuid to identify OF flows not associated - // with ovsdb rows. +struct uuid *hc_uuid = NULL; // uuid to identify OF flows not associated with + // ovsdb rows. + +struct simap localvif_to_ofport = SIMAP_INITIALIZER(&localvif_to_ofport); +struct hmap tunnels = HMAP_INITIALIZER(&tunnels); +static bool full_binding_processing = false; /* Maps from a chassis to the OpenFlow port number of the tunnel that can be * used to reach that chassis. */ @@ -60,6 +64,28 @@ struct chassis_tunnel { enum chassis_tunnel_type type; }; +void +localvif_to_ofports_clear(void) +{ + simap_clear(&localvif_to_ofport); +} + +void +tunnels_clear(void) +{ + struct chassis_tunnel *tun, *next; + HMAP_FOR_EACH_SAFE (tun, next, hmap_node, &tunnels) { + hmap_remove(&tunnels, &tun->hmap_node); + free(tun); + } +} + +static void +set_full_processing(void) +{ + full_binding_processing = true; +} + static struct chassis_tunnel * chassis_tunnel_find(struct hmap *tunnels, const char *chassis_id) { @@ -146,14 +172,417 @@ get_localnet_port(struct hmap *local_datapaths, int64_t tunnel_key) return ld ? ld->localnet_port : NULL; } + +static void +consider_physical(enum mf_field_id mff_ovn_geneve, + const struct simap *ct_zones, + struct hmap *local_datapaths, + struct hmap *patched_datapaths, + const struct sbrec_port_binding *binding, + struct ofpbuf *ofpacts_p, bool is_new) +{ + /* Skip the port binding if the port is on a datapath that is neither + * local nor with any logical patch port connected, because local ports + * would never need to talk to those ports. + * + * Even with this approach there could still be unnecessary port + * bindings processed. A better approach would be a kind of "flood + * fill" algorithm: + * + * 1. Initialize set S to the logical datapaths that have a port + * located on the hypervisor. + * + * 2. For each patch port P in a logical datapath in S, add the + * logical datapath of the remote end of P to S. Iterate + * until S reaches a fixed point. + * + * This can be implemented in northd, which can generate the sets and + * save it on each port-binding record in SB, and ovn-controller can + * use the information directly. However, there can be update storms + * when a pair of patch ports are added/removed to connect/disconnect + * large lrouters and lswitches. This need to be studied further. + */ + uint32_t dp_key = binding->datapath->tunnel_key; + uint32_t port_key = binding->tunnel_key; + if (!get_local_datapath(local_datapaths, dp_key) + && !get_patched_datapath(patched_datapaths, dp_key)) { + return; + } + + /* Find the OpenFlow port for the logical port, as 'ofport'. This is + * one of: + * + * - If the port is a VIF on the chassis we're managing, the + * OpenFlow port for the VIF. 'tun' will be NULL. + * + * The same logic handles logical patch ports, as well as + * localnet patch ports. + * + * For a container nested inside a VM and accessible via a VLAN, + * 'tag' is the VLAN ID; otherwise 'tag' is 0. + * + * For a localnet patch port, if a VLAN ID was configured, 'tag' + * is set to that VLAN ID; otherwise 'tag' is 0. + * + * - If the port is on a remote chassis, the OpenFlow port for a + * tunnel to the VIF's remote chassis. 'tun' identifies that + * tunnel. + */ + + int tag = 0; + ofp_port_t ofport; + bool is_remote = false; + if (binding->parent_port && *binding->parent_port) { + if (!binding->tag) { + return; + } + ofport = u16_to_ofp(simap_get(&localvif_to_ofport, + binding->parent_port)); + if (ofport) { + tag = *binding->tag; + } + } else { + ofport = u16_to_ofp(simap_get(&localvif_to_ofport, + binding->logical_port)); + if (!strcmp(binding->type, "localnet") && ofport && binding->tag) { + tag = *binding->tag; + } + } + + const struct chassis_tunnel *tun = NULL; + const struct sbrec_port_binding *localnet_port = + get_localnet_port(local_datapaths, dp_key); + if (!ofport) { + /* It is remote port, may be reached by tunnel or localnet port */ + is_remote = true; + if (!binding->chassis) { + return; + } + if (localnet_port) { + ofport = u16_to_ofp(simap_get(&localvif_to_ofport, + localnet_port->logical_port)); + if (!ofport) { + return; + } + } else { + tun = chassis_tunnel_find(&tunnels, binding->chassis->name); + if (!tun) { + return; + } + ofport = tun->ofport; + } + } + + struct match match; + if (!is_remote) { + int zone_id = simap_get(ct_zones, binding->logical_port); + /* Packets that arrive from a vif can belong to a VM or + * to a container located inside that VM. Packets that + * arrive from containers have a tag (vlan) associated with them. + */ + + /* Table 0, Priority 150 and 100. + * ============================== + * + * Priority 150 is for tagged traffic. This may be containers in a + * VM or a VLAN on a local network. For such traffic, match on the + * tags and then strip the tag. + * + * Priority 100 is for traffic belonging to VMs or untagged locally + * connected networks. + * + * For both types of traffic: set MFF_LOG_INPORT to the logical + * input port, MFF_LOG_DATAPATH to the logical datapath, and + * resubmit into the logical ingress pipeline starting at table + * 16. */ + ofpbuf_clear(ofpacts_p); + match_init_catchall(&match); + match_set_in_port(&match, ofport); + + /* 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")) { + match_set_dl_vlan(&match, htons(tag)); + ofpact_put_STRIP_VLAN(ofpacts_p); + } + + /* Remember the size with just strip vlan added so far, + * as we're going to remove this with ofpbuf_pull() later. */ + uint32_t ofpacts_orig_size = ofpacts_p->size; + + if (zone_id) { + put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); + } + + /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ + put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, ofpacts_p); + put_load(port_key, MFF_LOG_INPORT, 0, 32, ofpacts_p); + + /* Resubmit to first logical ingress pipeline table. */ + put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); + ofctrl_add_flow(OFTABLE_PHY_TO_LOG, + tag ? 150 : 100, &match, ofpacts_p, + &binding->header_.uuid, is_new); + + if (!tag && !strcmp(binding->type, "localnet")) { + /* Add a second flow for frames that lack any 802.1Q + * header. For these, drop the OFPACT_STRIP_VLAN + * action. */ + ofpbuf_pull(ofpacts_p, ofpacts_orig_size); + match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI)); + ofctrl_add_flow(0, 100, &match, ofpacts_p, + &binding->header_.uuid, is_new); + } + + /* 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 MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + + if (zone_id) { + put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); + } + + /* Resubmit to table 34. */ + put_resubmit(OFTABLE_DROP_LOOPBACK, ofpacts_p); + ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, + 100, &match, ofpacts_p, + &binding->header_.uuid, is_new); + + /* Table 34, Priority 100. + * ======================= + * + * Drop packets whose logical inport and outport are the same. */ + match_init_catchall(&match); + ofpbuf_clear(ofpacts_p); + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + ofctrl_add_flow(OFTABLE_DROP_LOOPBACK, 100, &match, ofpacts_p, + &binding->header_.uuid, is_new); + + /* Table 64, 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); + if (tag) { + /* For containers sitting behind a local vif, tag the packets + * before delivering them. */ + struct ofpact_vlan_vid *vlan_vid; + vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p); + vlan_vid->vlan_vid = tag; + vlan_vid->push_vlan_if_needed = true; + + /* A packet might need to hair-pin back into its ingress + * OpenFlow port (to a different logical port, which we already + * checked back in table 34), so set the in_port to zero. */ + put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p)); + put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); + } + ofpact_put_OUTPUT(ofpacts_p)->port = ofport; + if (tag) { + /* Revert the tag added to the packets headed to containers + * in the previous step. If we don't do this, the packets + * that are to be broadcasted to a VM in the same logical + * switch will also contain the tag. Also revert the zero'd + * in_port. */ + ofpact_put_STRIP_VLAN(ofpacts_p); + put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p)); + } + ofctrl_add_flow(OFTABLE_LOG_TO_PHY, 100, &match, ofpacts_p, + &binding->header_.uuid, is_new); + } else if (!tun) { + /* Remote port connected by localnet port */ + /* Table 33, priority 100. + * ======================= + * + * Implements switching to localnet port. Each flow matches a + * logical output port on remote hypervisor, switch the output port + * to connected localnet port and resubmits to same table. + */ + + match_init_catchall(&match); + ofpbuf_clear(ofpacts_p); + + /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + + put_load(localnet_port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); + + /* Resubmit to table 33. */ + put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p); + ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, &match, + ofpacts_p, &binding->header_.uuid, is_new); + } else { + /* Remote port connected by tunnel */ + /* Table 32, priority 100. + * ======================= + * + * Implements output to remote hypervisors. Each flow matches an + * output port that includes a logical port on a remote hypervisor, + * and tunnels the packet to that hypervisor. + */ + + match_init_catchall(&match); + ofpbuf_clear(ofpacts_p); + + /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + + put_encapsulation(mff_ovn_geneve, tun, binding->datapath, + port_key, ofpacts_p); + + /* Output to tunnel. */ + ofpact_put_OUTPUT(ofpacts_p)->port = ofport; + ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, &match, ofpacts_p, + &binding->header_.uuid, is_new); + } +} + +static void +consider_mc_group(enum mf_field_id mff_ovn_geneve, + const struct simap *ct_zones, + struct hmap *local_datapaths, + const struct sbrec_multicast_group *mc, + struct ofpbuf *ofpacts_p, + struct ofpbuf *remote_ofpacts_p, bool is_new) +{ + struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis); + struct match match; + + match_init_catchall(&match); + match_set_metadata(&match, htonll(mc->datapath->tunnel_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, mc->tunnel_key); + + /* Go through all of the ports in the multicast group: + * + * - For remote ports, add the chassis to 'remote_chassis'. + * + * - For local ports (other than logical patch ports), add actions + * to 'ofpacts' to set the output port and resubmit. + * + * - For logical patch ports, add actions to 'remote_ofpacts' + * instead. (If we put them in 'ofpacts', then the output + * would happen on every hypervisor in the multicast group, + * effectively duplicating the packet.) + */ + ofpbuf_clear(ofpacts_p); + ofpbuf_clear(remote_ofpacts_p); + for (size_t i = 0; i < mc->n_ports; i++) { + struct sbrec_port_binding *port = mc->ports[i]; + + if (port->datapath != mc->datapath) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, UUID_FMT": multicast group contains ports " + "in wrong datapath", + UUID_ARGS(&mc->header_.uuid)); + return; + } + + int zone_id = simap_get(ct_zones, port->logical_port); + if (zone_id) { + put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); + } + + if (!strcmp(port->type, "patch")) { + put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, + remote_ofpacts_p); + put_resubmit(OFTABLE_DROP_LOOPBACK, remote_ofpacts_p); + } else if (simap_contains(&localvif_to_ofport, + (port->parent_port && *port->parent_port) + ? port->parent_port : port->logical_port)) { + put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); + put_resubmit(OFTABLE_DROP_LOOPBACK, ofpacts_p); + } else if (port->chassis && !get_localnet_port(local_datapaths, + mc->datapath->tunnel_key)) { + /* Add remote chassis only when localnet port not exist, + * otherwise multicast will reach remote ports through localnet + * port. */ + sset_add(&remote_chassis, port->chassis->name); + } + } + + /* Table 33, priority 100. + * ======================= + * + * Handle output to the local logical ports in the multicast group, if + * any. */ + bool local_ports = ofpacts_p->size > 0; + if (local_ports) { + /* Following delivery to local logical ports, restore the multicast + * group as the logical output port. */ + put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); + + ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, + 100, &match, ofpacts_p, + &mc->header_.uuid, is_new); + } + + /* Table 32, priority 100. + * ======================= + * + * Handle output to the remote chassis in the multicast group, if + * any. */ + if (!sset_is_empty(&remote_chassis) || remote_ofpacts_p->size > 0) { + if (remote_ofpacts_p->size > 0) { + /* Following delivery to logical patch ports, restore the + * multicast group as the logical output port. */ + put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, + remote_ofpacts_p); + } + + const char *chassis; + const struct chassis_tunnel *prev = NULL; + SSET_FOR_EACH (chassis, &remote_chassis) { + const struct chassis_tunnel *tun + = chassis_tunnel_find(&tunnels, chassis); + if (!tun) { + return; + } + + if (!prev || tun->type != prev->type) { + put_encapsulation(mff_ovn_geneve, tun, mc->datapath, + mc->tunnel_key, remote_ofpacts_p); + prev = tun; + } + ofpact_put_OUTPUT(remote_ofpacts_p)->port = tun->ofport; + } + + if (remote_ofpacts_p->size) { + if (local_ports) { + put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p); + } + ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, + &match, remote_ofpacts_p, + &mc->header_.uuid, is_new); + } + } + sset_destroy(&remote_chassis); +} + void physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, const char *this_chassis_id, const struct simap *ct_zones, struct hmap *local_datapaths, struct hmap *patched_datapaths) { - struct simap localvif_to_ofport = SIMAP_INITIALIZER(&localvif_to_ofport); - struct hmap tunnels = HMAP_INITIALIZER(&tunnels); if (!hc_uuid) { hc_uuid = xmalloc(sizeof(struct uuid)); uuid_generate(hc_uuid); @@ -193,11 +622,21 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, bool is_patch = !strcmp(iface_rec->type, "patch"); if (is_patch && localnet) { /* localnet patch ports can be handled just like VIFs. */ - simap_put(&localvif_to_ofport, localnet, ofport); + struct simap_node *old = simap_find(&localvif_to_ofport, + localnet); + if (!old || old->data != ofport) { + simap_put(&localvif_to_ofport, localnet, ofport); + set_full_processing(); + } break; } else if (is_patch && logpatch) { /* Logical patch ports can be handled just like VIFs. */ - simap_put(&localvif_to_ofport, logpatch, ofport); + struct simap_node *old = simap_find(&localvif_to_ofport, + logpatch); + if (!old || old->data != ofport) { + simap_put(&localvif_to_ofport, logpatch, ofport); + set_full_processing(); + } break; } else if (chassis_id) { enum chassis_tunnel_type tunnel_type; @@ -214,18 +653,38 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, continue; } - struct chassis_tunnel *tun = xmalloc(sizeof *tun); - hmap_insert(&tunnels, &tun->hmap_node, - hash_string(chassis_id, 0)); - tun->chassis_id = chassis_id; - tun->ofport = u16_to_ofp(ofport); - tun->type = tunnel_type; + struct chassis_tunnel *old = chassis_tunnel_find(&tunnels, + chassis_id); + if (!old) { + struct chassis_tunnel *tun = xmalloc(sizeof *tun); + hmap_insert(&tunnels, &tun->hmap_node, + hash_string(chassis_id, 0)); + tun->chassis_id = chassis_id; + tun->ofport = u16_to_ofp(ofport); + tun->type = tunnel_type; + set_full_processing(); + } else { + ofp_port_t new_port = u16_to_ofp(ofport); + if (new_port != old->ofport) { + old->ofport = new_port; + set_full_processing(); + } + if (tunnel_type != old->type) { + old->type = tunnel_type; + set_full_processing(); + } + } break; } else { const char *iface_id = smap_get(&iface_rec->external_ids, "iface-id"); if (iface_id) { - simap_put(&localvif_to_ofport, iface_id, ofport); + struct simap_node *old = simap_find(&localvif_to_ofport, + iface_id); + if (!old || old->data != ofport) { + simap_put(&localvif_to_ofport, iface_id, ofport); + set_full_processing(); + } } } } @@ -237,396 +696,35 @@ 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; - SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { - /* Skip the port binding if the port is on a datapath that is neither - * local nor with any logical patch port connected, because local ports - * would never need to talk to those ports. - * - * Even with this approach there could still be unnecessary port - * bindings processed. A better approach would be a kind of "flood - * fill" algorithm: - * - * 1. Initialize set S to the logical datapaths that have a port - * located on the hypervisor. - * - * 2. For each patch port P in a logical datapath in S, add the - * logical datapath of the remote end of P to S. Iterate - * until S reaches a fixed point. - * - * This can be implemented in northd, which can generate the sets and - * save it on each port-binding record in SB, and ovn-controller can - * use the information directly. However, there can be update storms - * when a pair of patch ports are added/removed to connect/disconnect - * large lrouters and lswitches. This need to be studied further. - */ - uint32_t dp_key = binding->datapath->tunnel_key; - uint32_t port_key = binding->tunnel_key; - if (!get_local_datapath(local_datapaths, dp_key) - && !get_patched_datapath(patched_datapaths, dp_key)) { - continue; + if (full_binding_processing) { + SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { + consider_physical(mff_ovn_geneve, ct_zones, local_datapaths, + patched_datapaths, binding, &ofpacts, true); } - - /* Find the OpenFlow port for the logical port, as 'ofport'. This is - * one of: - * - * - If the port is a VIF on the chassis we're managing, the - * OpenFlow port for the VIF. 'tun' will be NULL. - * - * The same logic handles logical patch ports, as well as - * localnet patch ports. - * - * For a container nested inside a VM and accessible via a VLAN, - * 'tag' is the VLAN ID; otherwise 'tag' is 0. - * - * For a localnet patch port, if a VLAN ID was configured, 'tag' - * is set to that VLAN ID; otherwise 'tag' is 0. - * - * - If the port is on a remote chassis, the OpenFlow port for a - * tunnel to the VIF's remote chassis. 'tun' identifies that - * tunnel. - */ - - int tag = 0; - ofp_port_t ofport; - bool is_remote = false; - if (binding->parent_port && *binding->parent_port) { - if (!binding->tag) { + full_binding_processing = false; + } else { + SBREC_PORT_BINDING_FOR_EACH_TRACKED (binding, ctx->ovnsb_idl) { + bool is_deleted = sbrec_port_binding_row_get_seqno(binding, + OVSDB_IDL_CHANGE_DELETE) > 0; + bool is_new = sbrec_port_binding_row_get_seqno(binding, + OVSDB_IDL_CHANGE_MODIFY) == 0; + + if (is_deleted) { + ofctrl_remove_flows(&binding->header_.uuid); continue; } - ofport = u16_to_ofp(simap_get(&localvif_to_ofport, - binding->parent_port)); - if (ofport) { - tag = *binding->tag; - } - } else { - ofport = u16_to_ofp(simap_get(&localvif_to_ofport, - binding->logical_port)); - if (!strcmp(binding->type, "localnet") && ofport && binding->tag) { - tag = *binding->tag; - } - } - const struct chassis_tunnel *tun = NULL; - const struct sbrec_port_binding *localnet_port = - get_localnet_port(local_datapaths, dp_key); - if (!ofport) { - /* It is remote port, may be reached by tunnel or localnet port */ - is_remote = true; - if (!binding->chassis) { - continue; - } - if (localnet_port) { - ofport = u16_to_ofp(simap_get(&localvif_to_ofport, - localnet_port->logical_port)); - if (!ofport) { - continue; - } - } else { - tun = chassis_tunnel_find(&tunnels, binding->chassis->name); - if (!tun) { - continue; - } - ofport = tun->ofport; - } - } - - struct match match; - if (!is_remote) { - int zone_id = simap_get(ct_zones, binding->logical_port); - /* Packets that arrive from a vif can belong to a VM or - * to a container located inside that VM. Packets that - * arrive from containers have a tag (vlan) associated with them. - */ - - /* Table 0, Priority 150 and 100. - * ============================== - * - * Priority 150 is for tagged traffic. This may be containers in a - * VM or a VLAN on a local network. For such traffic, match on the - * tags and then strip the tag. - * - * Priority 100 is for traffic belonging to VMs or untagged locally - * connected networks. - * - * For both types of traffic: set MFF_LOG_INPORT to the logical - * input port, MFF_LOG_DATAPATH to the logical datapath, and - * resubmit into the logical ingress pipeline starting at table - * 16. */ - ofpbuf_clear(&ofpacts); - match_init_catchall(&match); - match_set_in_port(&match, ofport); - - /* 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")) { - match_set_dl_vlan(&match, htons(tag)); - ofpact_put_STRIP_VLAN(&ofpacts); - } - - /* Remember the size with just strip vlan added so far, - * as we're going to remove this with ofpbuf_pull() later. */ - uint32_t ofpacts_orig_size = ofpacts.size; - - if (zone_id) { - put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts); - } - - /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ - put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); - put_load(port_key, MFF_LOG_INPORT, 0, 32, &ofpacts); - - /* Resubmit to first logical ingress pipeline table. */ - put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); - ofctrl_add_flow(OFTABLE_PHY_TO_LOG, - tag ? 150 : 100, &match, &ofpacts, - &binding->header_.uuid, true); - - if (!tag && !strcmp(binding->type, "localnet")) { - /* Add a second flow for frames that lack any 802.1Q - * header. For these, drop the OFPACT_STRIP_VLAN - * action. */ - ofpbuf_pull(&ofpacts, ofpacts_orig_size); - match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI)); - ofctrl_add_flow(0, 100, &match, &ofpacts, - &binding->header_.uuid, true); - } - - /* 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); - - /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ - match_set_metadata(&match, htonll(dp_key)); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); - - if (zone_id) { - put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts); - } - - /* Resubmit to table 34. */ - put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts); - ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, &match, &ofpacts, - &binding->header_.uuid, true); - - /* Table 34, Priority 100. - * ======================= - * - * Drop packets whose logical inport and outport are the same. */ - match_init_catchall(&match); - ofpbuf_clear(&ofpacts); - match_set_metadata(&match, htonll(dp_key)); - match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); - ofctrl_add_flow(OFTABLE_DROP_LOOPBACK, 100, &match, &ofpacts, - &binding->header_.uuid, true); - - /* Table 64, Priority 100. - * ======================= - * - * Deliver the packet to the local vif. */ - match_init_catchall(&match); - ofpbuf_clear(&ofpacts); - match_set_metadata(&match, htonll(dp_key)); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); - if (tag) { - /* For containers sitting behind a local vif, tag the packets - * before delivering them. */ - struct ofpact_vlan_vid *vlan_vid; - vlan_vid = ofpact_put_SET_VLAN_VID(&ofpacts); - vlan_vid->vlan_vid = tag; - vlan_vid->push_vlan_if_needed = true; - - /* A packet might need to hair-pin back into its ingress - * OpenFlow port (to a different logical port, which we already - * checked back in table 34), so set the in_port to zero. */ - put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(&ofpacts)); - put_load(0, MFF_IN_PORT, 0, 16, &ofpacts); - } - ofpact_put_OUTPUT(&ofpacts)->port = ofport; - if (tag) { - /* Revert the tag added to the packets headed to containers - * in the previous step. If we don't do this, the packets - * that are to be broadcasted to a VM in the same logical - * switch will also contain the tag. Also revert the zero'd - * in_port. */ - ofpact_put_STRIP_VLAN(&ofpacts); - put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(&ofpacts)); - } - ofctrl_add_flow(OFTABLE_LOG_TO_PHY, 100, &match, &ofpacts, - &binding->header_.uuid, true); - } else if (!tun) { - /* Remote port connected by localnet port */ - /* Table 33, priority 100. - * ======================= - * - * Implements switching to localnet port. Each flow matches a - * logical output port on remote hypervisor, switch the output port - * to connected localnet port and resubmits to same table. - */ - - match_init_catchall(&match); - ofpbuf_clear(&ofpacts); - - /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ - match_set_metadata(&match, htonll(dp_key)); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); - - put_load(localnet_port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); - - /* Resubmit to table 33. */ - put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); - ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, &match, &ofpacts, - &binding->header_.uuid, true); - } else { - /* Remote port connected by tunnel */ - /* Table 32, priority 100. - * ======================= - * - * Implements output to remote hypervisors. Each flow matches an - * output port that includes a logical port on a remote hypervisor, - * and tunnels the packet to that hypervisor. - */ - - match_init_catchall(&match); - ofpbuf_clear(&ofpacts); - - /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ - match_set_metadata(&match, htonll(dp_key)); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); - - put_encapsulation(mff_ovn_geneve, tun, binding->datapath, - port_key, &ofpacts); - - /* Output to tunnel. */ - ofpact_put_OUTPUT(&ofpacts)->port = ofport; - ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, &match, &ofpacts, - &binding->header_.uuid, true); + consider_physical(mff_ovn_geneve, ct_zones, local_datapaths, + patched_datapaths, binding, &ofpacts, is_new); } } - /* Handle output to multicast groups, in tables 32 and 33. */ const struct sbrec_multicast_group *mc; struct ofpbuf remote_ofpacts; ofpbuf_init(&remote_ofpacts, 0); SBREC_MULTICAST_GROUP_FOR_EACH (mc, ctx->ovnsb_idl) { - struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis); - struct match match; - - match_init_catchall(&match); - match_set_metadata(&match, htonll(mc->datapath->tunnel_key)); - match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, mc->tunnel_key); - - /* Go through all of the ports in the multicast group: - * - * - For remote ports, add the chassis to 'remote_chassis'. - * - * - For local ports (other than logical patch ports), add actions - * to 'ofpacts' to set the output port and resubmit. - * - * - For logical patch ports, add actions to 'remote_ofpacts' - * instead. (If we put them in 'ofpacts', then the output - * would happen on every hypervisor in the multicast group, - * effectively duplicating the packet.) - */ - ofpbuf_clear(&ofpacts); - ofpbuf_clear(&remote_ofpacts); - for (size_t i = 0; i < mc->n_ports; i++) { - struct sbrec_port_binding *port = mc->ports[i]; - - if (port->datapath != mc->datapath) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, UUID_FMT": multicast group contains ports " - "in wrong datapath", - UUID_ARGS(&mc->header_.uuid)); - continue; - } - - int zone_id = simap_get(ct_zones, port->logical_port); - if (zone_id) { - put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts); - } - - if (!strcmp(port->type, "patch")) { - put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, - &remote_ofpacts); - put_resubmit(OFTABLE_DROP_LOOPBACK, &remote_ofpacts); - } else if (simap_contains(&localvif_to_ofport, - (port->parent_port && *port->parent_port) - ? port->parent_port : port->logical_port)) { - put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); - put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts); - } else if (port->chassis && !get_localnet_port(local_datapaths, - mc->datapath->tunnel_key)) { - /* Add remote chassis only when localnet port not exist, - * otherwise multicast will reach remote ports through localnet - * port. */ - sset_add(&remote_chassis, port->chassis->name); - } - } - - /* Table 33, priority 100. - * ======================= - * - * Handle output to the local logical ports in the multicast group, if - * any. */ - bool local_ports = ofpacts.size > 0; - if (local_ports) { - /* Following delivery to local logical ports, restore the multicast - * group as the logical output port. */ - put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); - - ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, &match, &ofpacts, - &mc->header_.uuid, true); - } - - /* Table 32, priority 100. - * ======================= - * - * Handle output to the remote chassis in the multicast group, if - * any. */ - if (!sset_is_empty(&remote_chassis) || remote_ofpacts.size > 0) { - if (remote_ofpacts.size > 0) { - /* Following delivery to logical patch ports, restore the - * multicast group as the logical output port. */ - put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, - &remote_ofpacts); - } - - const char *chassis; - const struct chassis_tunnel *prev = NULL; - SSET_FOR_EACH (chassis, &remote_chassis) { - const struct chassis_tunnel *tun - = chassis_tunnel_find(&tunnels, chassis); - if (!tun) { - continue; - } - - if (!prev || tun->type != prev->type) { - put_encapsulation(mff_ovn_geneve, tun, mc->datapath, - mc->tunnel_key, &remote_ofpacts); - prev = tun; - } - ofpact_put_OUTPUT(&remote_ofpacts)->port = tun->ofport; - } - - if (remote_ofpacts.size) { - if (local_ports) { - put_resubmit(OFTABLE_LOCAL_OUTPUT, &remote_ofpacts); - } - ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, - &match, &remote_ofpacts, - &mc->header_.uuid, true); - } - } - sset_destroy(&remote_chassis); + consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths, + mc, &ofpacts, &remote_ofpacts, true); } ofpbuf_uninit(&remote_ofpacts); @@ -687,17 +785,20 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { struct match match = MATCH_CATCHALL_INITIALIZER; + uint32_t dp_key = binding->datapath->tunnel_key; + uint32_t port_key = binding->tunnel_key; + if (!binding->chassis || strcmp(tun->chassis_id, binding->chassis->name)) { continue; } match_set_in_port(&match, tun->ofport); - match_set_tun_id(&match, htonll(binding->datapath->tunnel_key)); + match_set_tun_id(&match, htonll(dp_key)); ofpbuf_clear(&ofpacts); put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); - put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 15, &ofpacts); + put_load(port_key, MFF_LOG_INPORT, 0, 15, &ofpacts); put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts, @@ -733,11 +834,4 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, hc_uuid, true); ofpbuf_uninit(&ofpacts); - simap_destroy(&localvif_to_ofport); - struct chassis_tunnel *tun_next; - HMAP_FOR_EACH_SAFE (tun, tun_next, hmap_node, &tunnels) { - hmap_remove(&tunnels, &tun->hmap_node); - free(tun); - } - hmap_destroy(&tunnels); } diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h index 658b49f..be9fb74 100644 --- a/ovn/controller/physical.h +++ b/ovn/controller/physical.h @@ -45,5 +45,7 @@ void physical_run(struct controller_ctx *, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, const char *chassis_id, const struct simap *ct_zones, struct hmap *local_datapaths, struct hmap *patched_datapaths); +void localvif_to_ofports_clear(void); +void tunnels_clear(void); #endif /* ovn/physical.h */ -- 1.7.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev