On Wed, Nov 5, 2025 at 7:21 PM Han Zhou <[email protected]> wrote:
>
>
>
> 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,

Ack.  I'll add it as TODO in this file.  And the same for the comment
you've for p9.

Once I get some time,  I'll revisit this and move the code to the lib.

Thanks
Numan

>
> 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

Reply via email to