On Mon, Nov 3, 2025 at 6:19 PM <[email protected]> wrote: > > 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.
Yes it would be good to avoid redundant code. Perhaps we should add a comment in this file for this TODO. Otherwise, Acked-by: Han Zhou <[email protected]> > > 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 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
