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

Reply via email to