From: Numan Siddique <[email protected]> This commit processes the logical flows and converts them to OpenFlow rules. Much of the code is taken from controller/lflow.c.
At some point we should consider moving controller/lflow.c to lib/lflow.c and reuse it in both ovn-controller and ovn-br-controller. Acked-by: Mark Michelson <[email protected]> Signed-off-by: Numan Siddique <[email protected]> --- br-controller/en-lflow.c | 312 ++++++++++++++++++++++++++++++++++++++- br-controller/en-lflow.h | 3 + 2 files changed, 310 insertions(+), 5 deletions(-) diff --git a/br-controller/en-lflow.c b/br-controller/en-lflow.c index b1a6efbf5f..5024817be4 100644 --- a/br-controller/en-lflow.c +++ b/br-controller/en-lflow.c @@ -19,25 +19,327 @@ #include <stdio.h> /* OVS includes. */ +#include "lib/coverage.h" #include "openvswitch/vlog.h" /* OVN includes. */ +#include "br-flow-mgr.h" #include "en-lflow.h" +#include "en-bridge-data.h" +#include "include/ovn/actions.h" +#include "lib/inc-proc-eng.h" +#include "lib/lflow-conj-ids.h" +#include "lib/ovn-br-idl.h" VLOG_DEFINE_THIS_MODULE(en_lflow); +COVERAGE_DEFINE(lflow_run); + +struct lflow_output_persistent_data { + struct shash symtab; +}; + +struct ed_type_lflow_output_data { + /* conjunction ID usage information of lflows */ + struct conj_ids conj_ids; + + /* Data which is persistent and not cleared during + * full recompute. */ + struct lflow_output_persistent_data pd; +}; + +struct lflow_ctx_in { + struct shash *ovn_bridges; + struct shash *symtab; + struct ovnbrrec_logical_flow_table *brrec_lflow_table; +}; + +struct lflow_ctx_out { + struct conj_ids *conj_ids; +}; + +struct lookup_port_aux { + const struct ovnbrrec_logical_flow *lflow; + const struct ovn_bridge *br; +}; + +static struct expr *convert_match_to_expr( + const struct ovnbrrec_logical_flow *lflow, struct expr **prereqs, + struct shash *symtab); +static void init_lflow_ctx(struct engine_node *node, + struct ed_type_bridge_data *, + struct ed_type_lflow_output_data *, + struct lflow_ctx_in *, + struct lflow_ctx_out *); +static void lflow_run(struct lflow_ctx_in *, + struct lflow_ctx_out *); +static bool lookup_port_cb(const void *aux_, const char *port_name, + unsigned int *portp); +static void consider_logical_flow(const struct ovnbrrec_logical_flow *, + const struct ovn_bridge *, + struct lflow_ctx_in *, + struct lflow_ctx_out *); +static void add_matches_to_flow_table(const struct ovnbrrec_logical_flow *, + const struct ovn_bridge *, + struct hmap *matches, + uint8_t ptable, + uint8_t output_ptable, + struct ofpbuf *ovnacts); + void *en_lflow_output_init(struct engine_node *node OVS_UNUSED, - struct engine_arg *arg OVS_UNUSED) + struct engine_arg *arg OVS_UNUSED) { - return NULL; + struct ed_type_lflow_output_data *lflow_data = xzalloc(sizeof *lflow_data); + ovn_init_symtab(&lflow_data->pd.symtab); + lflow_conj_ids_init(&lflow_data->conj_ids); + + return lflow_data; } -void en_lflow_output_cleanup(void *data_ OVS_UNUSED) +void en_lflow_output_cleanup(void *data_) { + struct ed_type_lflow_output_data *lflow_data = data_; + lflow_conj_ids_destroy(&lflow_data->conj_ids); + expr_symtab_destroy(&lflow_data->pd.symtab); + shash_destroy(&lflow_data->pd.symtab); } enum engine_node_state -en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data OVS_UNUSED) +en_lflow_output_run(struct engine_node *node OVS_UNUSED, void *data_) +{ + struct ed_type_lflow_output_data *lflow_data = data_; + struct ed_type_bridge_data *bridge_data = + engine_get_input_data("bridge_data", node); + + lflow_conj_ids_clear(&lflow_data->conj_ids); + br_flow_switch_logical_oflow_tables(); + + struct lflow_ctx_in l_ctx_in; + struct lflow_ctx_out l_ctx_out; + + init_lflow_ctx(node, bridge_data, lflow_data, &l_ctx_in, &l_ctx_out); + lflow_run(&l_ctx_in, &l_ctx_out); + + return EN_UPDATED; +} + +/* Static functions. */ + +static void +init_lflow_ctx(struct engine_node *node, + struct ed_type_bridge_data *bridge_data, + struct ed_type_lflow_output_data *lflow_data, + struct lflow_ctx_in *l_ctx_in, + struct lflow_ctx_out *l_ctx_out) +{ + struct ovnbrrec_logical_flow_table *lflow_table = + (struct ovnbrrec_logical_flow_table *) EN_OVSDB_GET( + engine_get_input("BR_logical_flow", node)); + + l_ctx_in->ovn_bridges = &bridge_data->bridges; + l_ctx_in->brrec_lflow_table = lflow_table; + l_ctx_in->symtab = &lflow_data->pd.symtab; + l_ctx_out->conj_ids = &lflow_data->conj_ids; +} + +static void +lflow_run(struct lflow_ctx_in *l_ctx_in, struct lflow_ctx_out *l_ctx_out) +{ + COVERAGE_INC(lflow_run); + + const struct ovnbrrec_logical_flow *lflow; + OVNBRREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, l_ctx_in->brrec_lflow_table) { + struct ovn_bridge *br = shash_find_data(l_ctx_in->ovn_bridges, + lflow->bridge->name); + if (!br) { + continue; + } + + consider_logical_flow(lflow, br, l_ctx_in, l_ctx_out); + } +} + +static void +consider_logical_flow(const struct ovnbrrec_logical_flow *lflow, + const struct ovn_bridge *br, + struct lflow_ctx_in *l_ctx_in, + struct lflow_ctx_out *l_ctx_out) +{ + uint64_t ovnacts_stub[1024 / 8]; + struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub); + struct expr *prereqs = NULL; + + struct ovnact_parse_params pp = { + .symtab = l_ctx_in->symtab, + .pipeline = OVNACT_P_INGRESS, + .n_tables = 255, + .cur_ltable = lflow->table_id, + }; + + char *error = ovnacts_parse_string(lflow->actions, &pp, + &ovnacts, &prereqs); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", + lflow->actions, error); + free(error); + } + + struct hmap *matches = NULL; + struct expr *expr = NULL; + + expr = convert_match_to_expr(lflow, &prereqs, l_ctx_in->symtab); + if (!expr) { + goto done; + } + + expr = expr_normalize(expr); + + uint32_t start_conj_id = 0; + uint32_t n_conjs = 0; + + struct lookup_port_aux aux = { + .lflow = lflow, + .br = br, + }; + + matches = xmalloc(sizeof *matches); + n_conjs = expr_to_matches(expr, lookup_port_cb, &aux, matches); + if (n_conjs) { + start_conj_id = lflow_conj_ids_alloc(l_ctx_out->conj_ids, + &lflow->header_.uuid, + &br->key, n_conjs); + if (!start_conj_id) { + VLOG_ERR("32-bit conjunction ids exhausted!"); + goto done; + } + + expr_matches_prepare(matches, start_conj_id - 1); + } + + if (hmap_is_empty(matches)) { + VLOG_DBG("lflow "UUID_FMT" matches are empty, skip", + UUID_ARGS(&lflow->header_.uuid)); + goto done; + } + + uint8_t ptable = BR_OFTABLE_LOG_INGRESS_PIPELINE + lflow->table_id; + + add_matches_to_flow_table(lflow, br, matches, ptable, + BR_OFTABLE_SAVE_INPORT, + &ovnacts); + +done: + expr_destroy(expr); + expr_destroy(prereqs); + ovnacts_free(ovnacts.data, ovnacts.size); + ofpbuf_uninit(&ovnacts); + expr_matches_destroy(matches); + free(matches); +} + +/* Converts the match and returns the simplified expr tree. + * + * The caller should evaluate the conditions and normalize the expr tree. + * If parsing is successful, '*prereqs' is also consumed. + */ +static struct expr * +convert_match_to_expr(const struct ovnbrrec_logical_flow *lflow, + struct expr **prereqs, + struct shash *symtab) +{ + char *error = NULL; + + struct expr *e = expr_parse_string(lflow->match, symtab, NULL, NULL, NULL, + NULL, 0, &error); + if (!error) { + if (*prereqs) { + e = expr_combine(EXPR_T_AND, e, *prereqs); + *prereqs = NULL; + } + e = expr_annotate(e, symtab, &error); + } + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s", + lflow->match, error); + free(error); + return NULL; + } + + return expr_simplify(e); +} + +static bool +lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp) { - return EN_UNCHANGED; + if (!strcmp(port_name, "none")) { + *portp = 0; + return true; + } + + const struct lookup_port_aux *aux = aux_; + *portp = simap_get(&aux->br->ovs_ifaces, port_name); + + return *portp > 0; +} + +static void +add_matches_to_flow_table(const struct ovnbrrec_logical_flow *lflow, + const struct ovn_bridge *br, + struct hmap *matches, uint8_t ptable, + uint8_t output_ptable, struct ofpbuf *ovnacts) +{ + struct lookup_port_aux aux = { + .lflow = lflow, + .br = br, + }; + + /* Encode OVN logical actions into OpenFlow. */ + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + struct ovnact_encode_params ep = { + .lookup_port = lookup_port_cb, + .aux = &aux, + .is_switch = true, + .lflow_uuid = lflow->header_.uuid, + + .pipeline = OVNACT_P_INGRESS, + .ingress_ptable = BR_OFTABLE_LOG_INGRESS_PIPELINE, + .egress_ptable = 0, + .output_ptable = output_ptable, + }; + ovnacts_encode(ovnacts->data, ovnacts->size, &ep, &ofpacts); + + struct expr_match *m; + HMAP_FOR_EACH (m, hmap_node, matches) { + if (vector_is_empty(&m->conjunctions)) { + br_flow_add_logical_oflow(br->db_br->name, ptable, + lflow->priority, + lflow->header_.uuid.parts[0], + &m->match, &ofpacts, + &lflow->header_.uuid); + } else { + uint64_t conj_stubs[64 / 8]; + struct ofpbuf conj; + + ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs); + const struct cls_conjunction *src; + VECTOR_FOR_EACH_PTR (&m->conjunctions, src) { + struct ofpact_conjunction *dst = ofpact_put_CONJUNCTION(&conj); + dst->id = src->id; + dst->clause = src->clause; + dst->n_clauses = src->n_clauses; + } + + br_flow_add_logical_oflow(br->db_br->name, ptable, + lflow->priority, + lflow->header_.uuid.parts[0], + &m->match, &conj, + &lflow->header_.uuid); + ofpbuf_uninit(&conj); + } + } + + ofpbuf_uninit(&ofpacts); } diff --git a/br-controller/en-lflow.h b/br-controller/en-lflow.h index c3889cbdc9..39daa30174 100644 --- a/br-controller/en-lflow.h +++ b/br-controller/en-lflow.h @@ -9,6 +9,9 @@ #include "lib/inc-proc-eng.h" +#define BR_OFTABLE_LOG_INGRESS_PIPELINE 8 +#define BR_OFTABLE_SAVE_INPORT 64 + enum engine_node_state en_lflow_output_run(struct engine_node *, void *data); void *en_lflow_output_init(struct engine_node *node, struct engine_arg *arg); void en_lflow_output_cleanup(void *data); -- 2.51.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
