On Fri, Nov 7, 2025 at 2:00 PM Han Zhou <[email protected]> wrote:
>
>
>
> On Fri, Nov 7, 2025 at 10:46 AM Numan Siddique <[email protected]> wrote:
> >
> > On Thu, Nov 6, 2025 at 8:41 PM Han Zhou <[email protected]> wrote:
> > >
> > >
> > >
> > > On Thu, Nov 6, 2025 at 8:41 AM Numan Siddique <[email protected]> wrote:
> > > >
> > > > On Wed, Nov 5, 2025 at 6:52 PM Han Zhou <[email protected]> wrote:
> > > > >
> > > > >
> > > > >
> > > > > On Mon, Nov 3, 2025 at 6:19 PM <[email protected]> wrote:
> > > > > >
> > > > > > From: Numan Siddique <[email protected]>
> > > > > >
> > > > > > Signed-off-by: Numan Siddique <[email protected]>
> > > > > > ---
> > > > > >  br-controller/automake.mk   |    2 +
> > > > > >  br-controller/br-flow-mgr.c | 1263 
> > > > > > +++++++++++++++++++++++++++++++++++
> > > > > >  br-controller/br-flow-mgr.h |   65 ++
> > > > > >  3 files changed, 1330 insertions(+)
> > > > > >  create mode 100644 br-controller/br-flow-mgr.c
> > > > > >  create mode 100644 br-controller/br-flow-mgr.h
> > > > > >
> > > > > > diff --git a/br-controller/automake.mk b/br-controller/automake.mk
> > > > > > index b77e2abcff..ea515a85e4 100644
> > > > > > --- a/br-controller/automake.mk
> > > > > > +++ b/br-controller/automake.mk
> > > > > > @@ -1,5 +1,7 @@
> > > > > >  bin_PROGRAMS += br-controller/ovn-br-controller
> > > > > >  br_controller_ovn_br_controller_SOURCES = \
> > > > > > +       br-controller/br-flow-mgr.c \
> > > > > > +       br-controller/br-flow-mgr.h \
> > > > > >         br-controller/en-bridge-data.c \
> > > > > >         br-controller/en-bridge-data.h \
> > > > > >         br-controller/en-lflow.c \
> > > > > > diff --git a/br-controller/br-flow-mgr.c 
> > > > > > b/br-controller/br-flow-mgr.c
> > > > > > new file mode 100644
> > > > > > index 0000000000..cc9fb8f16d
> > > > > > --- /dev/null
> > > > > > +++ b/br-controller/br-flow-mgr.c
> > > > > > @@ -0,0 +1,1263 @@
> > > > > > +/*
> > > > > > + * Licensed under the Apache License, Version 2.0 (the "License");
> > > > > > + * you may not use this file except in compliance with the License.
> > > > > > + * You may obtain a copy of the License at:
> > > > > > + *
> > > > > > + *     http://www.apache.org/licenses/LICENSE-2.0
> > > > > > + *
> > > > > > + * Unless required by applicable law or agreed to in writing, 
> > > > > > software
> > > > > > + * distributed under the License is distributed on an "AS IS" 
> > > > > > BASIS,
> > > > > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
> > > > > > implied.
> > > > > > + * See the License for the specific language governing permissions 
> > > > > > and
> > > > > > + * limitations under the License.
> > > > > > + */
> > > > > > +
> > > > > > +#include <config.h>
> > > > > > +
> > > > > > +/* OVS includes. */
> > > > > > +#include "lib/byte-order.h"
> > > > > > +#include "lib/uuid.h"
> > > > > > +#include "lib/hash.h"
> > > > > > +#include "lib/hmapx.h"
> > > > > > +#include "lib/ovs-atomic.h"
> > > > > > +#include "openvswitch/ofp-actions.h"
> > > > > > +#include "openvswitch/ofp-flow.h"
> > > > > > +#include "openvswitch/ofp-group.h"
> > > > > > +#include "openvswitch/ofp-match.h"
> > > > > > +#include "openvswitch/ofp-msgs.h"
> > > > > > +#include "openvswitch/ofp-meter.h"
> > > > > > +#include "openvswitch/ofp-packet.h"
> > > > > > +#include "openvswitch/ofp-print.h"
> > > > > > +#include "openvswitch/ofp-util.h"
> > > > > > +#include "openvswitch/ofpbuf.h"
> > > > > > +#include "openvswitch/vlog.h"
> > > > > > +
> > > > > > +/* OVN includes. */
> > > > > > +#include "br-flow-mgr.h"
> > > > > > +
> > > > > > +VLOG_DEFINE_THIS_MODULE(brflowmgr);
> > > > > > +
> > > > > > +static struct hmap br_flow_tables = 
> > > > > > HMAP_INITIALIZER(&br_flow_tables);
> > > > > > +
> > > > > > +#define CONJ_ACT_COOKIE UINT64_MAX
> > > > > > +
> > > > > > +/* Flow handling. */
> > > > > > +struct ovn_flow;
> > > > > > +
> > > > > > +/* An ovn_flow action. */
> > > > > > +struct ovn_flow_action {
> > > > > > +    struct ovs_list flow_uuid_action_node;
> > > > > > +
> > > > > > +    struct ofpact *ofpacts;
> > > > > > +    size_t ofpacts_len;
> > > > > > +    uint64_t cookie;
> > > > > > +    struct uuid flow_uuid;
> > > > > > +    bool stale;
> > > > > > +
> > > > > > +    struct ovs_list list_node;
> > > > > > +    struct ovn_flow *flow;
> > > > > > +};
> > > > > > +
> > > > > > +/* An OpenFlow flow. */
> > > > > > +struct ovn_flow {
> > > > > > +    struct hmap_node hmap_node;
> > > > > > +    struct hmap *match_flow_table; /* Points to the match flow 
> > > > > > table hmap for
> > > > > > +                                    * easy access. */
> > > > > > +    struct hmap *uuid_action_flow_table; /* Points to the uuid 
> > > > > > action flow
> > > > > > +                                          * table hmap for easy 
> > > > > > access. */
> > > > > > +    struct ovs_list flow_list_node; /* List node in
> > > > > > +                                     * br_flow_table.flows_list[]. 
> > > > > > */
> > > > > > +
> > > > > > +    /* Flow list in which this 'ovn_flow.flow_list_node' is present
> > > > > > +     * (for easy access). */
> > > > > > +    struct ovs_list *flow_list;
> > > > > > +
> > > > > > +    /* Key. */
> > > > > > +    uint8_t table_id;
> > > > > > +    uint16_t priority;
> > > > > > +    struct minimatch match;
> > > > > > +
> > > > > > +    /* Hash. */
> > > > > > +    uint32_t hash;
> > > > > > +
> > > > > > +    /* Actions associated with the flow.
> > > > > > +     * An ovn_flow can have
> > > > > > +     *   - A normal action - has precedence over all the other 
> > > > > > below actions
> > > > > > +     *                       if set.
> > > > > > +     *   - a list of allow actions - has precedence over drop and 
> > > > > > conjunctive
> > > > > > +     *                               actions.
> > > > > > +     *   - a list of drop actions  - has precedence over 
> > > > > > conjunctive actions.
> > > > > > +     *   - a list of conjunctive actions.
> > > > > > +     */
> > > > >
> > > > > Is this overriding behavior documented anywhere for the users?
> > > >
> > > > Not really.  Presently if I understand correctly, ofctrl.c gives the
> > > > same precedence for multiple logical flows
> > > > which have the same match.  (Most likely in case of conjunction flows).
> > > >
> > > > For the provider brides,  I don't expect CMS to program such complex
> > > > flows using conjunction,  but you never know :).
> > > >
> > > >
> > > > >
> > > > > > +    struct ovn_flow_action *normal_act;
> > > > > > +    struct ovs_list allow_act_list;
> > > > > > +    struct ovs_list drop_act_list;
> > > > > > +    struct ovs_list conj_act_list;
> > > > > > +
> > > > > > +    /* Presently installed ofpacts. */
> > > > > > +    struct ofpact *installed_ofpacts;
> > > > > > +    size_t installed_ofpacts_len;
> > > > > > +
> > > > > > +    /* Cookie of the ovn_action which is presently active. */
> > > > > > +    uint64_t active_cookie;
> > > > > > +};
> > > > > > +
> > > > > > +/* Represents a list of 'ovn flow actions' associated with
> > > > > > + * a flow uuid. */
> > > > > > +struct flow_uuid_to_acts {
> > > > > > +    struct hmap_node hmap_node; /* Node in
> > > > > > +                                 * ovn_flow_table.uuid_flow_table. 
> > > > > > */
> > > > > > +    struct uuid flow_uuid;
> > > > > > +    struct ovs_list acts; /* A list of struct ovn_flow_action 
> > > > > > nodes that
> > > > > > +                           * are referenced by the uuid. */
> > > > > > +};
> > > > > > +
> > > > > > +/* Represents a flow table. */
> > > > > > +struct ovn_flow_table {
> > > > > > +    /* Hash map of flow table using flow match conditions as hash 
> > > > > > key.*/
> > > > > > +    struct hmap match_flow_table;
> > > > > > +
> > > > > > +    /* Hash map of ovn_flow_action list table using uuid as hash 
> > > > > > key.*/
> > > > > > +    struct hmap uuid_action_flow_table;
> > > > > > +};
> > > > > > +
> > > > > > +/* Represents a bridge flow table.
> > > > > > + *
> > > > > > + * We maintain 2 lists for openflows list. One represents an 
> > > > > > active flow
> > > > > > + * list and the other represents an old flow list.  When a flow is 
> > > > > > added to
> > > > > > + * the br flow table, it is always added to the active flow list. 
> > > > > > If that flow
> > > > > > + * is present in the old flow list, it is removed from the old one.
> > > > > > + *
> > > > > > + * The function br_flow_populate_oflow_msgs() clears all the
> > > > > > + * flows present in the old flow list.
> > > > > > + *
> > > > > > + * User can swap the lists by calling 
> > > > > > br_flow_switch_oflow_tables().
> > > > > > + *
> > > > > > + */
> > > > > > +struct br_flow_table {
> > > > > > +    struct hmap_node hmap_node;
> > > > > > +    char *bridge; /* key. */
> > > > > > +
> > > > > > +    struct ovn_flow_table lflow_table; /* For logical flows. */
> > > > > > +    struct ovn_flow_table pflow_table; /* For physical flows. */
> > > > > > +
> > > > > > +    struct ovs_list lflows_list[2];
> > > > > > +    struct ovs_list pflows_list[2];
> > > > > > +
> > > > > > +    struct ovs_list *active_lflows; /* Points to one of the 
> > > > > > element in
> > > > > > +                                     * lflows_list. */
> > > > > > +    struct ovs_list *old_lflows; /* Points to other element in
> > > > > > +                                  * lflows_list. */
> > > > > > +
> > > > > > +    struct ovs_list *active_pflows;
> > > > > > +    struct ovs_list *old_pflows;
> > > > > > +
> > > > > > +    struct hmapx modified_lflows;
> > > > > > +    struct hmapx modified_pflows;
> > > > > > +};
> > > > > > +
> > > > > > +/* Static functions. */
> > > > > > +static void br_flow_switch_lflow_table__(struct br_flow_table *);
> > > > > > +static void br_flow_switch_pflow_table__(struct br_flow_table *);
> > > > > > +static void br_flow_table_destroy__(struct br_flow_table *);
> > > > > > +
> > > > > > +static void br_flow_add_openflow__(struct br_flow_table *,
> > > > > > +                                   bool lflow_table,
> > > > > > +                                   uint8_t table_id, uint16_t 
> > > > > > priority,
> > > > > > +                                   uint64_t cookie, const struct 
> > > > > > match *match,
> > > > > > +                                   const struct ofpbuf *actions,
> > > > > > +                                   const struct uuid *flow_uuid);
> > > > > > +static void br_flows_remove(struct br_flow_table *, bool 
> > > > > > lflow_table,
> > > > > > +                            const struct uuid *flow_uuid);
> > > > > > +
> > > > > > +static void ovn_flow_table_init(struct ovn_flow_table *);
> > > > > > +static void ovn_flow_table_clear(struct ovn_flow_table *);
> > > > > > +static void ovn_flow_table_destroy(struct ovn_flow_table *);
> > > > > > +
> > > > > > +static uint32_t ovn_flow_match_hash__(uint8_t table_id, uint16_t 
> > > > > > priority,
> > > > > > +                                      const struct minimatch *);
> > > > > > +static uint32_t ovn_flow_match_hash(const struct ovn_flow *);
> > > > > > +
> > > > > > +static void ovn_flow_init(struct ovn_flow *, uint8_t table_id,
> > > > > > +                          uint16_t priority, const struct match *);
> > > > > > +static void ovn_flow_uninit(struct ovn_flow *);
> > > > > > +static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t 
> > > > > > priority,
> > > > > > +                                       const struct match *);
> > > > > > +static void ovn_flow_destroy(struct ovn_flow *);
> > > > > > +
> > > > > > +static struct ovn_flow_action *ovn_flow_action_alloc(const struct 
> > > > > > ofpbuf *,
> > > > > > +                                                     uint64_t 
> > > > > > cookie,
> > > > > > +                                                     const struct 
> > > > > > uuid *);
> > > > > > +static void ovn_flow_action_init(struct ovn_flow_action *,
> > > > > > +                                 const struct ofpbuf *, uint64_t 
> > > > > > cookie,
> > > > > > +                                 const struct uuid *);
> > > > > > +static void ovn_flow_action_destroy(struct ovn_flow_action *);
> > > > > > +static struct ovn_flow *ovn_flow_lookup(struct ovn_flow_table *,
> > > > > > +                                        uint8_t table_id, uint16_t 
> > > > > > priority,
> > > > > > +                                        const struct minimatch *);
> > > > > > +static void ovn_flow_invalidate_all_actions(struct ovn_flow *f);
> > > > > > +static void ovn_flow_clear_actions_from_list(struct ovs_list 
> > > > > > *act_list);
> > > > > > +static void ovn_flow_invalidate_actions_from_list(struct ovs_list 
> > > > > > *act_list);
> > > > > > +static void ovn_flow_clear_actions(struct ovn_flow *);
> > > > > > +static bool ovn_flow_has_active_actions(struct ovn_flow *);
> > > > > > +
> > > > > > +static bool ovn_flow_action_is_normal(const struct ovn_flow_action 
> > > > > > *);
> > > > > > +static bool ovn_flow_action_is_allow(const struct ovn_flow_action 
> > > > > > *);
> > > > > > +static bool ovn_flow_action_is_drop(const struct ovn_flow_action 
> > > > > > *);
> > > > > > +static bool ovn_flow_action_is_conj(const struct ovn_flow_action 
> > > > > > *);
> > > > > > +
> > > > > > +static struct flow_uuid_to_acts *flow_uuid_to_acts_lookup(
> > > > > > +    struct hmap *uuid_action_table, const struct uuid *flow_uuid);
> > > > > > +static void ovn_flow_action_link_to_flow_uuid(
> > > > > > +    struct hmap *uuid_action_table, struct ovn_flow_action *);
> > > > > > +static void ovn_flow_unref_action__(struct ovn_flow *,
> > > > > > +                                    struct ovn_flow_action *);
> > > > > > +static struct ovn_flow_action * 
> > > > > > ovn_flow_action_get_matching_in_list(
> > > > > > +    struct ovs_list *act_list, struct ovn_flow_action *a);
> > > > > > +static void ovn_flow_unref_action(struct ovn_flow *, struct 
> > > > > > ovn_flow_action *);
> > > > > > +static struct ovn_flow_action *ovn_flow_get_matching_action(
> > > > > > +    struct ovn_flow *, struct ovn_flow_action *);
> > > > > > +
> > > > > > +static bool ovn_flow_update_actions(struct ovn_flow_table *,
> > > > > > +                                    struct ovn_flow *,
> > > > > > +                                    struct ovn_flow_action *);
> > > > > > +
> > > > > > +static void ovn_flow_prepare_ofmsg(struct ovn_flow *, struct 
> > > > > > ovs_list *msgs);
> > > > > > +static void br_flow_populate_oflow_msgs__(struct br_flow_table *,
> > > > > > +                                          struct ovs_list *msgs);
> > > > > > +
> > > > > > +static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
> > > > > > +static void add_flow_mod(struct ofputil_flow_mod *, struct 
> > > > > > ovs_list *msgs);
> > > > > > +static void ovn_flow_add_oflow(struct ovn_flow *, struct 
> > > > > > ovn_flow_action *,
> > > > > > +                               struct ovs_list *msgs);
> > > > > > +static void ovn_flow_mod_oflow(struct ovn_flow *, struct 
> > > > > > ovn_flow_action *,
> > > > > > +                               struct ovs_list *msgs);
> > > > > > +static void ovn_flow_del_oflow(struct ovn_flow *, struct ovs_list 
> > > > > > *msgs);
> > > > > > +static void ovn_flow_log(const struct ovn_flow *);
> > > > > > +static char * ovn_flow_to_string(const struct ovn_flow *);
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_tables_init(void)
> > > > > > +{
> > > > > > +
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_tables_destroy(void)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_table;
> > > > > > +    HMAP_FOR_EACH_POP (br_table, hmap_node, &br_flow_tables) {
> > > > > > +        br_flow_table_destroy__(br_table);
> > > > > > +    }
> > > > > > +
> > > > > > +    hmap_destroy(&br_flow_tables);
> > > > > > +}
> > > > > > +
> > > > > > +struct br_flow_table *
> > > > > > +br_flow_table_get(const char *bridge)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_table;
> > > > > > +    uint32_t hash = hash_string(bridge, 0);
> > > > > > +    HMAP_FOR_EACH_WITH_HASH (br_table, hmap_node, hash, 
> > > > > > &br_flow_tables) {
> > > > > > +        if (!strcmp(br_table->bridge, bridge)) {
> > > > > > +            return br_table;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    return NULL;
> > > > > > +}
> > > > > > +
> > > > > > +struct br_flow_table *
> > > > > > +br_flow_table_alloc(const char *bridge)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_table = xzalloc(sizeof *br_table);
> > > > > > +    ovn_flow_table_init(&br_table->lflow_table);
> > > > > > +    ovn_flow_table_init(&br_table->pflow_table);
> > > > > > +
> > > > > > +    ovs_list_init(&br_table->lflows_list[0]);
> > > > > > +    ovs_list_init(&br_table->lflows_list[1]);
> > > > > > +
> > > > > > +    ovs_list_init(&br_table->pflows_list[0]);
> > > > > > +    ovs_list_init(&br_table->pflows_list[1]);
> > > > > > +
> > > > > > +    hmapx_init(&br_table->modified_lflows);
> > > > > > +    hmapx_init(&br_table->modified_pflows);
> > > > > > +
> > > > > > +    br_table->active_lflows = &br_table->lflows_list[0];
> > > > > > +    br_table->old_lflows = &br_table->lflows_list[1];
> > > > > > +
> > > > > > +    br_table->active_pflows = &br_table->pflows_list[0];
> > > > > > +    br_table->old_pflows = &br_table->pflows_list[1];
> > > > > > +
> > > > > > +    br_table->bridge = xstrdup(bridge);
> > > > > > +    hmap_insert(&br_flow_tables, &br_table->hmap_node, 
> > > > > > hash_string(bridge, 0));
> > > > > > +    return br_table;
> > > > > > +}
> > > > > > +
> > > > > > +void br_flow_table_destroy(const char *bridge)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_table = br_flow_table_get(bridge);
> > > > > > +    if (br_table) {
> > > > > > +        hmap_remove(&br_flow_tables, &br_table->hmap_node);
> > > > > > +        br_flow_table_destroy__(br_table);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_switch_logical_oflow_tables(void)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable;
> > > > > > +    HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) {
> > > > > > +        br_flow_switch_lflow_table__(br_ftable);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_switch_logical_oflow_table(const char *bridge)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable;
> > > > > > +    br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (br_ftable) {
> > > > > > +        br_flow_switch_lflow_table__(br_ftable);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_switch_physical_oflow_tables(void)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable;
> > > > > > +    HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) {
> > > > > > +        br_flow_switch_pflow_table__(br_ftable);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_switch_physical_oflow_table(const char *bridge)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable;
> > > > > > +    br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (br_ftable) {
> > > > > > +        br_flow_switch_pflow_table__(br_ftable);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_add_logical_oflow(const char *bridge, uint8_t table_id,
> > > > > > +                          uint16_t priority, uint64_t cookie,
> > > > > > +                          const struct match *match,
> > > > > > +                          const struct ofpbuf *actions,
> > > > > > +                          const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (!br_ftable) {
> > > > > > +        br_ftable = br_flow_table_alloc(bridge);
> > > > > > +    }
> > > > > > +
> > > > > > +    br_flow_add_openflow__(br_ftable, true, table_id, priority, 
> > > > > > cookie, match,
> > > > > > +                           actions, flow_uuid);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_add_physical_oflow(const char *bridge, uint8_t table_id,
> > > > > > +                           uint16_t priority, uint64_t cookie,
> > > > > > +                           const struct match *match,
> > > > > > +                           const struct ofpbuf *actions,
> > > > > > +                           const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (!br_ftable) {
> > > > > > +        br_ftable = br_flow_table_alloc(bridge);
> > > > > > +    }
> > > > > > +
> > > > > > +    br_flow_add_openflow__(br_ftable, false, table_id, priority, 
> > > > > > cookie, match,
> > > > > > +                           actions, flow_uuid);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_remove_logical_oflows_all(const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable;
> > > > > > +    HMAP_FOR_EACH (br_ftable, hmap_node, &br_flow_tables) {
> > > > > > +        br_flows_remove(br_ftable, true, flow_uuid);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_remove_logical_oflows(const char *bridge, const struct 
> > > > > > uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (br_ftable) {
> > > > > > +        br_flows_remove(br_ftable, true, flow_uuid);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_remove_physical_oflows(const char *bridge,
> > > > > > +                               const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (br_ftable) {
> > > > > > +        br_flows_remove(br_ftable, false, flow_uuid);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_flush_all_oflows(void)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_table;
> > > > > > +    HMAP_FOR_EACH_POP (br_table, hmap_node, &br_flow_tables) {
> > > > > > +        br_flow_table_destroy__(br_table);
> > > > > > +    }
> > > > > > +
> > > > > > +    hmap_destroy(&br_flow_tables);
> > > > > > +    hmap_init(&br_flow_tables);
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_flush_oflows(const char *bridge)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (br_ftable) {
> > > > > > +        hmap_remove(&br_flow_tables, &br_ftable->hmap_node);
> > > > > > +        br_flow_table_destroy__(br_ftable);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +void
> > > > > > +br_flow_populate_oflow_msgs(const char *bridge, struct ovs_list 
> > > > > > *msgs)
> > > > > > +{
> > > > > > +    struct br_flow_table *br_ftable = br_flow_table_get(bridge);
> > > > > > +    if (br_ftable) {
> > > > > > +        br_flow_populate_oflow_msgs__(br_ftable, msgs);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +/* Static functions. */
> > > > > > +
> > > > > > +static void
> > > > > > +br_flow_switch_lflow_table__(struct br_flow_table *br_ftable)
> > > > > > +{
> > > > > > +    struct ovs_list *t = br_ftable->active_lflows;
> > > > > > +    br_ftable->active_lflows = br_ftable->old_lflows;
> > > > > > +    br_ftable->old_lflows = t;
> > > > > > +
> > > > > > +    hmapx_clear(&br_ftable->modified_lflows);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +br_flow_switch_pflow_table__(struct br_flow_table *br_ftable)
> > > > > > +{
> > > > > > +    struct ovs_list *t = br_ftable->active_pflows;
> > > > > > +    br_ftable->active_pflows = br_ftable->old_pflows;
> > > > > > +    br_ftable->old_pflows = t;
> > > > > > +
> > > > > > +    hmapx_clear(&br_ftable->modified_pflows);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +br_flow_table_destroy__(struct br_flow_table *br_ftable)
> > > > > > +{
> > > > > > +    hmapx_clear(&br_ftable->modified_lflows);
> > > > > > +    hmapx_destroy(&br_ftable->modified_lflows);
> > > > > > +    ovn_flow_table_destroy(&br_ftable->lflow_table);
> > > > > > +
> > > > > > +    hmapx_clear(&br_ftable->modified_pflows);
> > > > > > +    hmapx_destroy(&br_ftable->modified_pflows);
> > > > > > +    ovn_flow_table_destroy(&br_ftable->pflow_table);
> > > > > > +
> > > > > > +    free(br_ftable->bridge);
> > > > > > +    free(br_ftable);
> > > > > > +}
> > > > > > +
> > > > > > +/* This function
> > > > > > + *   - Looks up in the bridge flow table if the flow F
> > > > > > + *     with the provided (match, table_id, priority) is already
> > > > > > + *     present or not.
> > > > > > + *
> > > > > > + *   - If not present it creates an ovn_flow 'F' with the provided
> > > > > > + *     (match, table_id, priority) and inserts into the flow table.
> > > > > > + *
> > > > > > + *   - Allocates ovn_flow_action 'A' with the given -
> > > > > > + *     (actions, cookie, flow_uuid).
> > > > > > + *
> > > > > > + *   - If 'F' already has 'A' it does nothing and returns. 
> > > > > > Otherwise
> > > > > > + *     it associates 'A' to the 'F' actions.
> > > > > > + *
> > > > > > + *   - Adds 'F' to the active flow list.
> > > > > > + */
> > > > > > +static void
> > > > > > +br_flow_add_openflow__(struct br_flow_table *br_ftable, bool 
> > > > > > lflow_table,
> > > > > > +                       uint8_t table_id, uint16_t priority,
> > > > > > +                       uint64_t cookie, const struct match *match,
> > > > > > +                       const struct ofpbuf *actions,
> > > > > > +                       const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct ovn_flow_table *ftable;
> > > > > > +    struct hmapx *modified_flows;
> > > > > > +    struct ovs_list *active_flows;
> > > > > > +
> > > > > > +    if (lflow_table) {
> > > > > > +        ftable = &br_ftable->lflow_table;
> > > > > > +        modified_flows = &br_ftable->modified_lflows;
> > > > > > +        active_flows = br_ftable->active_lflows;
> > > > > > +    } else {
> > > > > > +        ftable = &br_ftable->pflow_table;
> > > > > > +        modified_flows = &br_ftable->modified_pflows;
> > > > > > +        active_flows = br_ftable->active_pflows;
> > > > > > +    }
> > > > > > +
> > > > > > +    struct minimatch minimatch;
> > > > > > +    minimatch_init(&minimatch, match);
> > > > > > +
> > > > > > +    struct ovn_flow *f = ovn_flow_lookup(ftable, table_id, 
> > > > > > priority,
> > > > > > +                                         &minimatch);
> > > > > > +
> > > > > > +    minimatch_destroy(&minimatch);
> > > > > > +
> > > > > > +    bool active_flow_list_changed = false;
> > > > > > +
> > > > > > +    if (!f) {
> > > > > > +        f = ovn_flow_alloc(table_id, priority, match);
> > > > > > +        f->match_flow_table = &ftable->match_flow_table;
> > > > > > +        f->uuid_action_flow_table = 
> > > > > > &ftable->uuid_action_flow_table;
> > > > > > +        ovs_list_init(&f->flow_list_node);
> > > > > > +        hmap_insert(&ftable->match_flow_table, &f->hmap_node,
> > > > > > +                    f->hash);
> > > > > > +    } else {
> > > > > > +        ovs_list_remove(&f->flow_list_node);
> > > > > > +        active_flow_list_changed = (active_flows != f->flow_list);
> > > > > > +    }
> > > > > > +
> > > > > > +    f->flow_list = active_flows;
> > > > > > +    ovs_list_push_back(active_flows, &f->flow_list_node);
> > > > > > +
> > > > > > +    struct ovn_flow_action *a = ovn_flow_action_alloc(actions, 
> > > > > > cookie,
> > > > > > +                                                      flow_uuid);
> > > > > > +
> > > > > > +    if (active_flow_list_changed) {
> > > > > > +        ovn_flow_invalidate_all_actions(f);
> > > > > > +    }
> > > > > > +
> > > > > > +    struct ovn_flow_action *existing_a =
> > > > > > +        f ? ovn_flow_get_matching_action(f, a): NULL;
> > > > > > +    bool push_flow_to_switch = true;
> > > > > > +
> > > > > > +    if (existing_a && uuid_equals(&existing_a->flow_uuid, 
> > > > > > flow_uuid)) {
> > > > > > +        existing_a->stale = false;
> > > > > > +
> > > > > > +        if (!active_flow_list_changed) {
> > > > > > +            /* The flow-action pair already exists. Nothing to be 
> > > > > > done. */
> > > > > > +            push_flow_to_switch = false;
> > > > > > +        }
> > > > > > +        ovn_flow_action_destroy(a);
> > > > > > +    } else {
> > > > > > +        ovn_flow_update_actions(ftable, f, a);
> > > > > > +    }
> > > > > > +
> > > > > > +    if (push_flow_to_switch) {
> > > > > > +        ovn_flow_log(f);
> > > > > > +        hmapx_add(modified_flows, (void *) f);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +br_flows_remove(struct br_flow_table *br_ftable, bool lflow_table,
> > > > > > +                const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct ovn_flow_table *flow_table;
> > > > > > +    struct hmapx *modified_flows;
> > > > > > +
> > > > > > +    if (lflow_table) {
> > > > > > +        flow_table = &br_ftable->lflow_table;
> > > > > > +        modified_flows = &br_ftable->modified_lflows;
> > > > > > +    } else {
> > > > > > +        flow_table = &br_ftable->pflow_table;
> > > > > > +        modified_flows = &br_ftable->modified_pflows;
> > > > > > +    }
> > > > > > +
> > > > > > +    struct flow_uuid_to_acts *f_uuid_to_acts =
> > > > > > +        
> > > > > > flow_uuid_to_acts_lookup(&flow_table->uuid_action_flow_table,
> > > > > > +                                 flow_uuid);
> > > > > > +
> > > > > > +    if (!f_uuid_to_acts) {
> > > > > > +        return;
> > > > > > +    }
> > > > > > +
> > > > > > +    struct ovn_flow_action *a;
> > > > > > +    LIST_FOR_EACH_POP (a, flow_uuid_action_node, 
> > > > > > &f_uuid_to_acts->acts) {
> > > > > > +        struct ovn_flow *f = a->flow;
> > > > > > +        ovn_flow_unref_action__(a->flow, a);
> > > > > > +        ovn_flow_action_destroy(a);
> > > > > > +        hmapx_add(modified_flows, (void *) f);
> > > > > > +    }
> > > > > > +
> > > > > > +    hmap_remove(&flow_table->uuid_action_flow_table,
> > > > > > +                &f_uuid_to_acts->hmap_node);
> > > > > > +    free(f_uuid_to_acts);
> > > > > > +}
> > > > > > +
> > > > > > +/* ovn flow table functions. */
> > > > > > +static void
> > > > > > +ovn_flow_table_init(struct ovn_flow_table *flow_table)
> > > > > > +{
> > > > > > +    hmap_init(&flow_table->match_flow_table);
> > > > > > +    hmap_init(&flow_table->uuid_action_flow_table);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_table_clear(struct ovn_flow_table *flow_table)
> > > > > > +{
> > > > > > +    struct flow_uuid_to_acts *f_uuid_to_acts;
> > > > > > +    HMAP_FOR_EACH_POP (f_uuid_to_acts, hmap_node,
> > > > > > +                       &flow_table->uuid_action_flow_table) {
> > > > > > +        struct ovn_flow_action *a;
> > > > > > +        LIST_FOR_EACH_POP (a, flow_uuid_action_node,
> > > > > > +                           &f_uuid_to_acts->acts) {
> > > > > > +            ovn_flow_unref_action__(a->flow, a);
> > > > > > +            ovn_flow_action_destroy(a);
> > > > > > +        }
> > > > > > +
> > > > > > +        free(f_uuid_to_acts);
> > > > > > +    }
> > > > > > +
> > > > > > +    struct ovn_flow *f;
> > > > > > +    HMAP_FOR_EACH_POP (f, hmap_node, 
> > > > > > &flow_table->match_flow_table) {
> > > > > > +        ovn_flow_destroy(f);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_table_destroy(struct ovn_flow_table *flow_table)
> > > > > > +{
> > > > > > +    ovn_flow_table_clear(flow_table);
> > > > > > +    hmap_destroy(&flow_table->match_flow_table);
> > > > > > +    hmap_destroy(&flow_table->uuid_action_flow_table);
> > > > > > +}
> > > > > > +
> > > > > > +static uint32_t
> > > > > > +ovn_flow_match_hash__(uint8_t table_id, uint16_t priority,
> > > > > > +                       const struct minimatch *match)
> > > > > > +{
> > > > > > +    return hash_2words((table_id << 16) | priority,
> > > > > > +                       minimatch_hash(match, 0));
> > > > > > +}
> > > > > > +
> > > > > > +/* Returns a hash of the match key in 'f'. */
> > > > > > +static uint32_t
> > > > > > +ovn_flow_match_hash(const struct ovn_flow *f)
> > > > > > +{
> > > > > > +    return ovn_flow_match_hash__(f->table_id, f->priority, 
> > > > > > &f->match);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_action_init(struct ovn_flow_action *a, const struct 
> > > > > > ofpbuf *actions,
> > > > > > +                     uint64_t cookie, const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    a->ofpacts = xmemdup(actions->data, actions->size);
> > > > > > +    a->ofpacts_len = actions->size;
> > > > > > +    a->cookie = cookie;
> > > > > > +    a->flow_uuid = *flow_uuid;
> > > > > > +    a->stale = false;
> > > > > > +    ovs_list_init(&a->list_node);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_action_destroy(struct ovn_flow_action *a)
> > > > > > +{
> > > > > > +    free(a->ofpacts);
> > > > > > +    free(a);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t 
> > > > > > priority,
> > > > > > +              const struct match *match)
> > > > > > +{
> > > > > > +    f->table_id = table_id;
> > > > > > +    f->priority = priority;
> > > > > > +    minimatch_init(&f->match, match);
> > > > > > +
> > > > > > +    f->hash = ovn_flow_match_hash(f);
> > > > > > +
> > > > > > +    ovs_list_init(&f->allow_act_list);
> > > > > > +    ovs_list_init(&f->drop_act_list);
> > > > > > +    ovs_list_init(&f->conj_act_list);
> > > > > > +
> > > > > > +    f->installed_ofpacts = NULL;
> > > > > > +    f->installed_ofpacts_len = 0;
> > > > > > +
> > > > > > +    f->active_cookie = 0;
> > > > > > +}
> > > > > > +
> > > > > > +static struct ovn_flow *
> > > > > > +ovn_flow_alloc(uint8_t table_id, uint16_t priority,
> > > > > > +               const struct match *match)
> > > > > > +{
> > > > > > +    struct ovn_flow *f = xzalloc(sizeof *f);
> > > > > > +    ovn_flow_init(f, table_id, priority, match);
> > > > > > +
> > > > > > +    return f;
> > > > > > +}
> > > > > > +
> > > > > > +static struct ovn_flow_action *
> > > > > > +ovn_flow_action_alloc(const struct ofpbuf *actions,
> > > > > > +                      uint64_t cookie, const struct uuid 
> > > > > > *flow_uuid)
> > > > > > +{
> > > > > > +    struct ovn_flow_action *a = xzalloc(sizeof *a);
> > > > > > +    ovn_flow_action_init(a, actions, cookie, flow_uuid);
> > > > > > +
> > > > > > +    return a;
> > > > > > +}
> > > > > > +
> > > > > > +/* Finds and returns a ovn_flow in 'flow_table' whose key is 
> > > > > > identical to
> > > > > > + * 'target''s key, or NULL if there is none.
> > > > > > + */
> > > > > > +static struct ovn_flow *
> > > > > > +ovn_flow_lookup(struct ovn_flow_table *flow_table,
> > > > > > +                uint8_t table_id, uint16_t priority,
> > > > > > +                const struct minimatch *match)
> > > > > > +{
> > > > > > +    size_t hash = ovn_flow_match_hash__(table_id, priority, match);
> > > > > > +
> > > > > > +    struct ovn_flow *f;
> > > > > > +    HMAP_FOR_EACH_WITH_HASH (f, hmap_node, hash,
> > > > > > +                             &flow_table->match_flow_table) {
> > > > > > +        if (f->priority == priority && minimatch_equal(&f->match, 
> > > > > > match)) {
> > > > > > +            return f;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    return NULL;
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_clear_actions_from_list(struct ovs_list *act_list)
> > > > > > +{
> > > > > > +    struct ovn_flow_action *a;
> > > > > > +    LIST_FOR_EACH_POP (a, list_node, act_list) {
> > > > > > +        ovs_list_remove(&a->flow_uuid_action_node);
> > > > > > +        ovn_flow_action_destroy(a);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_invalidate_actions_from_list(struct ovs_list *act_list)
> > > > > > +{
> > > > > > +    struct ovn_flow_action *a;
> > > > > > +    LIST_FOR_EACH (a, list_node, act_list) {
> > > > > > +        a->stale = true;
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_clear_actions(struct ovn_flow *f)
> > > > > > +{
> > > > > > +    if (f->normal_act) {
> > > > > > +        ovs_list_remove(&f->normal_act->flow_uuid_action_node);
> > > > > > +        ovn_flow_action_destroy(f->normal_act);
> > > > > > +        f->normal_act = NULL;
> > > > > > +    }
> > > > > > +
> > > > > > +    ovn_flow_clear_actions_from_list(&f->allow_act_list);
> > > > > > +    ovn_flow_clear_actions_from_list(&f->drop_act_list);
> > > > > > +    ovn_flow_clear_actions_from_list(&f->conj_act_list);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_invalidate_all_actions(struct ovn_flow *f)
> > > > > > +{
> > > > > > +    if (f->normal_act) {
> > > > > > +        f->normal_act->stale = true;
> > > > > > +    }
> > > > > > +
> > > > > > +    ovn_flow_invalidate_actions_from_list(&f->allow_act_list);
> > > > > > +    ovn_flow_invalidate_actions_from_list(&f->drop_act_list);
> > > > > > +    ovn_flow_invalidate_actions_from_list(&f->conj_act_list);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_delete_stale_actions_from_list(struct ovs_list *act_list)
> > > > > > +{
> > > > > > +    struct ovn_flow_action *a, *next;
> > > > > > +    LIST_FOR_EACH_SAFE (a, next, list_node, act_list) {
> > > > > > +        if (a->stale) {
> > > > > > +            ovn_flow_unref_action(a->flow, a);
> > > > > > +        }
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_delete_stale_actions(struct ovn_flow *f)
> > > > > > +{
> > > > > > +    if (f->normal_act && f->normal_act->stale) {
> > > > > > +        ovn_flow_unref_action(f, f->normal_act);
> > > > > > +        f->normal_act = NULL;
> > > > > > +    }
> > > > > > +
> > > > > > +    ovn_flow_delete_stale_actions_from_list(&f->allow_act_list);
> > > > > > +    ovn_flow_delete_stale_actions_from_list(&f->drop_act_list);
> > > > > > +    ovn_flow_delete_stale_actions_from_list(&f->conj_act_list);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_uninit(struct ovn_flow *f)
> > > > > > +{
> > > > > > +    minimatch_destroy(&f->match);
> > > > > > +    ovn_flow_clear_actions(f);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_destroy(struct ovn_flow *f)
> > > > > > +{
> > > > > > +    if (f) {
> > > > > > +        ovn_flow_uninit(f);
> > > > > > +        free(f->installed_ofpacts);
> > > > > > +        free(f);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_action_is_drop(const struct ovn_flow_action *f)
> > > > > > +{
> > > > > > +    return f->ofpacts_len == 0;
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_action_is_conj(const struct ovn_flow_action *f)
> > > > > > +{
> > > > > > +    const struct ofpact *a = NULL;
> > > > > > +
> > > > > > +    OFPACT_FOR_EACH (a, f->ofpacts, f->ofpacts_len) {
> > > > > > +        if (a->type == OFPACT_CONJUNCTION) {
> > > > > > +            return true;
> > > > > > +        }
> > > > > > +    }
> > > > > > +    return false;
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_action_is_allow(const struct ovn_flow_action *f)
> > > > > > +{
> > > > > > +    if (ovn_flow_action_is_drop(f)) {
> > > > > > +        return false;
> > > > > > +    }
> > > > > > +
> > > > > > +    const struct ofpact *a = f->ofpacts;
> > > > > > +    return (a->type == OFPACT_RESUBMIT &&
> > > > > > +            ofpact_last(a, f->ofpacts, f->ofpacts_len));
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_action_is_normal(const struct ovn_flow_action *f)
> > > > > > +{
> > > > > > +    return (!ovn_flow_action_is_allow(f) && 
> > > > > > !ovn_flow_action_is_drop(f) &&
> > > > > > +            !ovn_flow_action_is_conj(f));
> > > > > > +}
> > > > > > +
> > > > > > +static struct ovn_flow_action *
> > > > > > +ovn_flow_action_get_matching_in_list(struct ovs_list *act_list,
> > > > > > +                                     struct ovn_flow_action *a)
> > > > > > +{
> > > > > > +    struct ovn_flow_action *act_in_list;
> > > > > > +    LIST_FOR_EACH (act_in_list, list_node, act_list) {
> > > > > > +        if (ofpacts_equal(act_in_list->ofpacts,
> > > > > > +                          act_in_list->ofpacts_len,
> > > > > > +                          a->ofpacts,
> > > > > > +                          a->ofpacts_len)) {
> > > > > > +            return act_in_list;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    return NULL;
> > > > > > +}
> > > > > > +
> > > > > > +static struct ovn_flow_action *
> > > > > > +ovn_flow_get_matching_action(struct ovn_flow *f, struct 
> > > > > > ovn_flow_action *a)
> > > > > > +{
> > > > > > +    if (f->normal_act && ovn_flow_action_is_normal(a)) {
> > > > > > +        if (ofpacts_equal(f->normal_act->ofpacts,
> > > > > > +                         f->normal_act->ofpacts_len,
> > > > > > +                         a->ofpacts,
> > > > > > +                         a->ofpacts_len)) {
> > > > > > +            return f->normal_act;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    if (ovn_flow_action_is_allow(a)) {
> > > > > > +        return 
> > > > > > ovn_flow_action_get_matching_in_list(&f->allow_act_list, a);
> > > > > > +    }
> > > > > > +
> > > > > > +    if (ovn_flow_action_is_drop(a)) {
> > > > > > +        return 
> > > > > > ovn_flow_action_get_matching_in_list(&f->drop_act_list, a);
> > > > > > +    }
> > > > > > +
> > > > > > +    if (ovn_flow_action_is_conj(a)) {
> > > > > > +        return 
> > > > > > ovn_flow_action_get_matching_in_list(&f->conj_act_list, a);
> > > > > > +    }
> > > > > > +
> > > > > > +    return NULL;
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_has_active_actions(struct ovn_flow *f)
> > > > > > +{
> > > > > > +    if (f->normal_act) {
> > > > > > +        return true;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (!ovs_list_is_empty(&f->allow_act_list)) {
> > > > > > +        return true;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (!ovs_list_is_empty(&f->drop_act_list)) {
> > > > > > +        return true;
> > > > > > +    }
> > > > > > +
> > > > > > +    return !ovs_list_is_empty(&f->conj_act_list);
> > > > > > +}
> > > > > > +
> > > > > > +static struct flow_uuid_to_acts *
> > > > > > +flow_uuid_to_acts_lookup(struct hmap *uuid_action_table,
> > > > > > +                         const struct uuid *flow_uuid)
> > > > > > +{
> > > > > > +    struct flow_uuid_to_acts *f_uuid_to_acts;
> > > > > > +    HMAP_FOR_EACH_WITH_HASH (f_uuid_to_acts, hmap_node, 
> > > > > > uuid_hash(flow_uuid),
> > > > > > +                             uuid_action_table) {
> > > > > > +        if (uuid_equals(flow_uuid, &f_uuid_to_acts->flow_uuid)) {
> > > > > > +            return f_uuid_to_acts;
> > > > > > +        }
> > > > > > +    }
> > > > > > +    return NULL;
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_action_link_to_flow_uuid(struct hmap *uuid_action_table,
> > > > > > +                                  struct ovn_flow_action *a)
> > > > > > +{
> > > > > > +    struct flow_uuid_to_acts *f_uuid_to_acts;
> > > > > > +    f_uuid_to_acts = flow_uuid_to_acts_lookup(uuid_action_table,
> > > > > > +                                              &a->flow_uuid);
> > > > > > +    if (!f_uuid_to_acts) {
> > > > > > +        f_uuid_to_acts = xzalloc(sizeof *f_uuid_to_acts);
> > > > > > +        f_uuid_to_acts->flow_uuid = a->flow_uuid;
> > > > > > +        ovs_list_init(&f_uuid_to_acts->acts);
> > > > > > +        hmap_insert(uuid_action_table, &f_uuid_to_acts->hmap_node,
> > > > > > +                    uuid_hash(&a->flow_uuid));
> > > > > > +    }
> > > > > > +
> > > > > > +    ovs_list_push_back(&f_uuid_to_acts->acts, 
> > > > > > &a->flow_uuid_action_node);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_unref_action__(struct ovn_flow *f, struct ovn_flow_action 
> > > > > > *a)
> > > > > > +{
> > > > > > +    if (f->normal_act == a) {
> > > > > > +        f->normal_act = NULL;
> > > > > > +    } else if (!ovs_list_is_empty(&a->list_node)) {
> > > > > > +        ovs_list_remove(&a->list_node);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_unref_action(struct ovn_flow *f, struct ovn_flow_action 
> > > > > > *a)
> > > > > > +{
> > > > > > +    ovs_list_remove(&a->flow_uuid_action_node);
> > > > > > +    ovn_flow_unref_action__(f, a);
> > > > > > +    ovn_flow_action_destroy(a);
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_update_actions(struct ovn_flow_table *ftable,
> > > > > > +                        struct ovn_flow *f, struct ovn_flow_action 
> > > > > > *a)
> > > > > > +{
> > > > > > +    bool flow_updated = true;
> > > > > > +    if (ovn_flow_action_is_normal(a)) {
> > > > > > +        if (f->normal_act) {
> > > > > > +            ovn_flow_unref_action(f, f->normal_act);
> > > > > > +        }
> > > > > > +        f->normal_act = a;
> > > > > > +    } else if (ovn_flow_action_is_allow(a)) {
> > > > > > +        flow_updated = ovs_list_is_empty(&f->allow_act_list);
> > > > >
> > > > > If I understand correctly, these lists allow_act_list, drop_act_list, 
> > > > > etc. are to handle conflict flow additions that have the same match 
> > > > > but different actions. So, if there is already an existing allow 
> > > > > action, here we are adding another to the list, should this flow be 
> > > > > considered as updated? But with the above logic, the flow_updated 
> > > > > would be false.
> > > >
> > > > We don't need to consider it as updated.  Let's say we have 2 Logical
> > > > flows L1 and L2 which both result in the same openflow match and have
> > > > the allow (or next;) action.
> > -
> > > > When L2 is created after L1,  we don't have to update the flow in the
> > > > switch because the openflow already exists.  This code stores the
> > > > "allow" action of L2 in the F's allow_act_list.
> > > > Later if L1 is deleted,  we still want to have the same openflow rule
> > > > present because of L2.
> >
> > > >
> > >
> > > I see. So this is just an optimization so that in such cases it is not 
> > > considered as updated so that we don't need to call handled_modified. 
> > > However, I just noticed that the return value of this function is never 
> > > checked, unfortunately, probably a miss?
> >
> > Thanks for pointing this out.  I think the function
> > ovn_flow_update_actions() can return void.  I'll change it.
> > >
> > > In addition, I think there is another situation that may end up with 
> > > inconsistency between the desired and the actual flows. If there are two 
> > > flows with the same match but different "normal" actions: flow1 -> 
> > > action1, flow2 -> action2. For normal actions, only one of them is saved. 
> > > So if flow2 is added after flow1, action1 is replaced by action2 for the 
> > > match. Then if flow2 is deleted, action2 is destroyed and the flow 
> > > doesn't have any actions so it will be destroyed. But in fact flow1 still 
> > > exists in the DB and we should honor action1 here. Correct?
> >
> > Right now, this won't be a problem as the ovn-br-controller always
> > does a recompute.  I'll work as follow up patches to add I-P.
> > I think we can keep the code in br-flow-mgr.c to AS IS for now.
> >
> Ok, then shall we
>
> > Also as per your suggestion in patch 1 to add indexing for the
> > "Logical_Flow" table,
> > I tried by adding like below
> >
> > ---
> > "indexes": [["bridge"], ["table_id"], ["priority"], ["match"]],
> > ---
> >
> > but ovsdb-server was failing to add more than one row to the
> > Logical_Flow table for a given Bridge.
> >
> > ---
> > $ovn-brctl add-flow br-provider 1 1000 "ip4 && ip4.dst == 10.0.0.10"
> > "ip4.src = 10.0.0.5; output;"
> > $ovn-brctl add-flow br-provider 1 1000 "ip4 && ip4.dst == 10.0.0.4"
> > "ip4.src = 10.0.0.5; output;"
> > 2025-11-07T18:41:30Z|00002|ovsdb_idl|WARN|transaction error:
> > {"details":"Transaction causes multiple rows in \"Logical_Flow\" table
> > to have identical values (a27cdd9d-39f3-430e-a980-de420913265a) for
> > index on column \"bridge\".  First row, with UUID
> > 511827a7-7461-4fe6-96f9-86a1f42fea1b, existed in the database before
> > this transaction and was not modified by the transaction.  Second row,
> > with UUID a9c4c4c8-a1e4-40dd-84b6-d2f8d6cca717, was inserted by this
> > transaction.","error":"constraint violation"}
> > ovn-brctl: transaction error: {"details":"Transaction causes multiple
> > rows in \"Logical_Flow\" table to have identical values
> > (a27cdd9d-39f3-430e-a980-de420913265a) for index on column \"bridge\".
> > First row, with UUID 511827a7-7461-4fe6-96f9-86a1f42fea1b, existed in
> > the database before this transaction and was not modified by the
> > transaction.  Second row, with UUID
> > a9c4c4c8-a1e4-40dd-84b6-d2f8d6cca717, was inserted by this
> > transaction.","error":"constraint violation"}
> > ------
> >
> > So for now we can remove the indexing.   It would be the
> > responsibility of CMS to not add duplicate logical flows.
>
> I think the index should be defined as:
>
> "indexes": [["bridge", "table_id", "priority", "match"]],
>
> because it is one compond index with multiple columns, and it shouldn't 
> result in contraint violation if the match column is different in the above 
> example. The way you defined was multiple single column indexes.
>
> I think this index is valuable because it can catch obvious CMS/user mistakes.

My bad.  Sounds good.  I'll add the indexed this way.

>
> However, it is not 100% guaranteed because the match is a string and can have 
> different format/spaces while symatically identical. So the logic for 
> handling conflict actions of same match is still needed.
>
> >
> > For the case where  CMS adds 2 or more  different logical flows with
> > the same match, but with different actions,  ovn-br-controller will
> > only consider one action out of many.
> > For the case you mentioned, we need to also store multiple normal
> > actions in "struct  ovn_lflow"; which is possible.  And this can be
> > addressed when we add I-P for logical flow changes.
> >
>
> Ok, I'd suggest add a comment in the code for this follow-up, since it is a 
> tricky corner case and easy to miss when supporting I-P.

Ack.  I'll add a comment.

Thanks
Numan

>
> Thanks,
> Han
>
> > Thanks
> > Numan
> >
> >
> >
> > >
> > > Regards,
> > > Han
> > >
> > > > Let me know if you've any further questions.
> > > >
> > > > Numan
> > > >
> > > > >
> > > > > Best regards,
> > > > > Han
> > > > >
> > > > > > +        ovs_list_push_back(&f->allow_act_list, &a->list_node);
> > > > > > +    } else if (ovn_flow_action_is_drop(a)) {
> > > > > > +        flow_updated = ovs_list_is_empty(&f->drop_act_list);
> > > > > > +        ovs_list_push_back(&f->drop_act_list, &a->list_node);
> > > > > > +    } else {
> > > > > > +        /* conj action. */
> > > > > > +        ovs_list_push_back(&f->conj_act_list, &a->list_node);
> > > > > > +    }
> > > > > > +
> > > > > > +    a->flow = f;
> > > > > > +    a->stale = false;
> > > > > > +
> > > > > > +    
> > > > > > ovn_flow_action_link_to_flow_uuid(&ftable->uuid_action_flow_table, 
> > > > > > a);
> > > > > > +
> > > > > > +    return flow_updated;
> > > > > > +}
> > > > > > +
> > > > > > +static bool
> > > > > > +ovn_flow_needs_flow_mod(struct ovn_flow *f, struct ovn_flow_action 
> > > > > > *a)
> > > > > > +{
> > > > > > +    if (f->installed_ofpacts_len != a->ofpacts_len) {
> > > > > > +        return true;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (f->installed_ofpacts_len && a->ofpacts_len) {
> > > > > > +        return !ofpacts_equal(f->installed_ofpacts,
> > > > > > +                              f->installed_ofpacts_len,
> > > > > > +                              a->ofpacts,
> > > > > > +                              a->ofpacts_len);
> > > > > > +    }
> > > > > > +
> > > > > > +    return f->active_cookie ? false : true;
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_prepare_ofmsg(struct ovn_flow *f, struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    struct ovn_flow_action *a = NULL;
> > > > > > +    struct ovn_flow_action *conj_act = NULL;
> > > > > > +
> > > > > > +    if (f->normal_act) {
> > > > > > +        a = f->normal_act;
> > > > > > +    } else if (!ovs_list_is_empty(&f->allow_act_list)) {
> > > > > > +        a = CONTAINER_OF(ovs_list_front(&f->allow_act_list),
> > > > > > +                         struct ovn_flow_action,
> > > > > > +                         list_node);
> > > > > > +    } else if (!ovs_list_is_empty(&f->drop_act_list)) {
> > > > > > +        a = CONTAINER_OF(ovs_list_front(&f->drop_act_list),
> > > > > > +                         struct ovn_flow_action,
> > > > > > +                         list_node);
> > > > > > +    } else if (!ovs_list_is_empty(&f->conj_act_list)) {
> > > > > > +        struct ovn_flow_action *c;
> > > > > > +        uint64_t ofpacts_stub[1024 / 8];
> > > > > > +        struct ofpbuf ofpacts = 
> > > > > > OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> > > > > > +
> > > > > > +        LIST_FOR_EACH (c, list_node, &f->conj_act_list) {
> > > > > > +            ofpbuf_put(&ofpacts, c->ofpacts, c->ofpacts_len);
> > > > > > +        }
> > > > > > +        conj_act = xzalloc(sizeof *conj_act);
> > > > > > +        conj_act->cookie = CONJ_ACT_COOKIE;
> > > > > > +        conj_act->ofpacts = xmemdup(ofpacts.data, ofpacts.size);
> > > > > > +        conj_act->ofpacts_len = ofpacts.size;
> > > > > > +        ofpbuf_uninit(&ofpacts);
> > > > > > +        a = conj_act;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (a) {
> > > > > > +        /* check if there is really a need to install or not. */
> > > > > > +        if (ovn_flow_needs_flow_mod(f, a)) {
> > > > > > +            if (f->active_cookie == 0) {
> > > > > > +                ovn_flow_add_oflow(f, a, msgs);
> > > > > > +            } else {
> > > > > > +                ovn_flow_mod_oflow(f, a, msgs);
> > > > > > +            }
> > > > > > +            free(f->installed_ofpacts);
> > > > > > +            if (a->ofpacts_len) {
> > > > > > +                f->installed_ofpacts = xmemdup(a->ofpacts, 
> > > > > > a->ofpacts_len);
> > > > > > +                f->installed_ofpacts_len = a->ofpacts_len;
> > > > > > +            } else {
> > > > > > +                f->installed_ofpacts = NULL;
> > > > > > +                f->installed_ofpacts_len = 0;
> > > > > > +            }
> > > > > > +        }
> > > > > > +    } else {
> > > > > > +        ovn_flow_del_oflow(f, msgs);
> > > > > > +        free(f->installed_ofpacts);
> > > > > > +        f->installed_ofpacts = NULL;
> > > > > > +        f->installed_ofpacts_len = 0;
> > > > > > +        f->active_cookie = 0;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (conj_act) {
> > > > > > +        free(conj_act->ofpacts);
> > > > > > +        free(conj_act);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_handle_modified(struct ovn_flow *f, struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    ovn_flow_delete_stale_actions(f);
> > > > > > +    ovn_flow_prepare_ofmsg(f, msgs);
> > > > > > +    if (!ovn_flow_has_active_actions(f)) {
> > > > > > +        hmap_remove(f->match_flow_table, &f->hmap_node);
> > > > > > +        ovs_list_remove(&f->flow_list_node);
> > > > > > +        ovn_flow_destroy(f);
> > > > > > +    }
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +br_flow_populate_oflow_msgs__(struct br_flow_table *br_ftable,
> > > > > > +                              struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    struct ovn_flow *f;
> > > > > > +
> > > > > > +    LIST_FOR_EACH_POP (f, flow_list_node, br_ftable->old_lflows) {
> > > > > > +        ovn_flow_clear_actions(f);
> > > > > > +        ovn_flow_prepare_ofmsg(f, msgs);
> > > > > > +        hmap_remove(f->match_flow_table, &f->hmap_node);
> > > > > > +        hmapx_find_and_delete(&br_ftable->modified_lflows, f);
> > > > > > +        ovn_flow_destroy(f);
> > > > > > +    }
> > > > > > +
> > > > > > +    LIST_FOR_EACH_POP (f, flow_list_node, br_ftable->old_pflows) {
> > > > > > +        ovn_flow_clear_actions(f);
> > > > > > +        ovn_flow_prepare_ofmsg(f, msgs);
> > > > > > +        hmap_remove(f->match_flow_table, &f->hmap_node);
> > > > > > +        hmapx_find_and_delete(&br_ftable->modified_pflows, f);
> > > > > > +        ovn_flow_destroy(f);
> > > > > > +    }
> > > > > > +
> > > > > > +    struct hmapx_node *node;
> > > > > > +    HMAPX_FOR_EACH (node, &br_ftable->modified_lflows) {
> > > > > > +        f = node->data;
> > > > > > +        ovn_flow_handle_modified(f, msgs);
> > > > > > +    }
> > > > > > +
> > > > > > +    hmapx_clear(&br_ftable->modified_lflows);
> > > > > > +
> > > > > > +    HMAPX_FOR_EACH (node, &br_ftable->modified_pflows) {
> > > > > > +        f = node->data;
> > > > > > +        ovn_flow_handle_modified(f, msgs);
> > > > > > +    }
> > > > > > +
> > > > > > +    hmapx_clear(&br_ftable->modified_pflows);
> > > > > > +}
> > > > > > +
> > > > > > +/* Flow table update. */
> > > > > > +
> > > > > > +static struct ofpbuf *
> > > > > > +encode_flow_mod(struct ofputil_flow_mod *fm)
> > > > > > +{
> > > > > > +    fm->buffer_id = UINT32_MAX;
> > > > > > +    fm->out_port = OFPP_ANY;
> > > > > > +    fm->out_group = OFPG_ANY;
> > > > > > +    return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF15_OXM);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    struct ofpbuf *msg = encode_flow_mod(fm);
> > > > > > +    ovs_list_push_back(msgs, &msg->list_node);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_add_oflow(struct ovn_flow *f, struct ovn_flow_action *a,
> > > > > > +                   struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    /* Send flow_mod to add flow. */
> > > > > > +    struct ofputil_flow_mod fm = {
> > > > > > +        .match = f->match,
> > > > > > +        .priority = f->priority,
> > > > > > +        .table_id = f->table_id,
> > > > > > +        .ofpacts = a->ofpacts,
> > > > > > +        .ofpacts_len = a->ofpacts_len,
> > > > > > +        .new_cookie = htonll(a->cookie),
> > > > > > +        .command = OFPFC_ADD,
> > > > > > +    };
> > > > > > +    add_flow_mod(&fm, msgs);
> > > > > > +    f->active_cookie = a->cookie;
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_mod_oflow(struct ovn_flow *f, struct ovn_flow_action *a,
> > > > > > +                   struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    /* Update actions in installed flow. */
> > > > > > +    struct ofputil_flow_mod fm = {
> > > > > > +        .match = f->match,
> > > > > > +        .priority = f->priority,
> > > > > > +        .table_id = f->table_id,
> > > > > > +        .ofpacts = a->ofpacts,
> > > > > > +        .ofpacts_len = a->ofpacts_len,
> > > > > > +        .command = OFPFC_MODIFY_STRICT,
> > > > > > +    };
> > > > > > +    /* Update cookie if it is changed. */
> > > > > > +    if (f->active_cookie != a->cookie) {
> > > > > > +        fm.modify_cookie = true;
> > > > > > +        fm.new_cookie = htonll(a->cookie);
> > > > > > +        /* Use OFPFC_ADD so that cookie can be updated. */
> > > > > > +        fm.command = OFPFC_ADD;
> > > > > > +    }
> > > > > > +    add_flow_mod(&fm, msgs);
> > > > > > +    f->active_cookie = a->cookie;
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_del_oflow(struct ovn_flow *f, struct ovs_list *msgs)
> > > > > > +{
> > > > > > +    struct ofputil_flow_mod fm = {
> > > > > > +        .match = f->match,
> > > > > > +        .priority = f->priority,
> > > > > > +        .table_id = f->table_id,
> > > > > > +        .command = OFPFC_DELETE_STRICT,
> > > > > > +    };
> > > > > > +    add_flow_mod(&fm, msgs);
> > > > > > +    f->active_cookie = 0;
> > > > > > +}
> > > > > > +
> > > > > > +static char *
> > > > > > +ovn_flow_to_string(const struct ovn_flow *f)
> > > > > > +{
> > > > > > +    struct ds s = DS_EMPTY_INITIALIZER;
> > > > > > +
> > > > > > +    struct ovn_flow_action *a = NULL;
> > > > > > +    struct ovn_flow_action *conj_act = NULL;
> > > > > > +
> > > > > > +    if (f->normal_act) {
> > > > > > +        a = f->normal_act;
> > > > > > +    } else if (!ovs_list_is_empty(&f->allow_act_list)) {
> > > > > > +        a = CONTAINER_OF(ovs_list_front(&f->allow_act_list),
> > > > > > +                         struct ovn_flow_action,
> > > > > > +                         list_node);
> > > > > > +    } else if (!ovs_list_is_empty(&f->drop_act_list)) {
> > > > > > +        a = CONTAINER_OF(ovs_list_front(&f->drop_act_list),
> > > > > > +                         struct ovn_flow_action,
> > > > > > +                         list_node);
> > > > > > +    } else if (!ovs_list_is_empty(&f->conj_act_list)) {
> > > > > > +        struct ovn_flow_action *c;
> > > > > > +        uint64_t ofpacts_stub[1024 / 8];
> > > > > > +        struct ofpbuf ofpacts = 
> > > > > > OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> > > > > > +
> > > > > > +        LIST_FOR_EACH (c, list_node, &f->conj_act_list) {
> > > > > > +            ofpbuf_put(&ofpacts, c->ofpacts, c->ofpacts_len);
> > > > > > +        }
> > > > > > +        conj_act = xzalloc(sizeof *conj_act);
> > > > > > +        conj_act->cookie = CONJ_ACT_COOKIE;
> > > > > > +        conj_act->ofpacts = xmemdup(ofpacts.data, ofpacts.size);
> > > > > > +        conj_act->ofpacts_len = ofpacts.size;
> > > > > > +        ofpbuf_uninit(&ofpacts);
> > > > > > +        a = conj_act;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (a) {
> > > > > > +        ds_put_format(&s, "cookie=%"PRIx64", ", a->cookie);
> > > > > > +    }
> > > > > > +    ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
> > > > > > +    ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
> > > > > > +    minimatch_format(&f->match, NULL, NULL, &s, 
> > > > > > OFP_DEFAULT_PRIORITY);
> > > > > > +    ds_put_cstr(&s, ", actions=");
> > > > > > +    struct ofpact_format_params fp = { .s = &s };
> > > > > > +    if (a) {
> > > > > > +        ofpacts_format(a->ofpacts, a->ofpacts_len, &fp);
> > > > > > +    } else {
> > > > > > +        ds_put_cstr(&s, "<EMPTY>");
> > > > > > +    }
> > > > > > +
> > > > > > +    if (conj_act) {
> > > > > > +        free(conj_act->ofpacts);
> > > > > > +        free(conj_act);
> > > > > > +    }
> > > > > > +    return ds_steal_cstr(&s);
> > > > > > +}
> > > > > > +
> > > > > > +static void
> > > > > > +ovn_flow_log(const struct ovn_flow *f)
> > > > > > +{
> > > > > > +    if (VLOG_IS_DBG_ENABLED()) {
> > > > > > +        char *s = ovn_flow_to_string(f);
> > > > > > +        VLOG_DBG("flow: %s", s);
> > > > > > +        free(s);
> > > > > > +    }
> > > > > > +}
> > > > > > diff --git a/br-controller/br-flow-mgr.h 
> > > > > > b/br-controller/br-flow-mgr.h
> > > > > > new file mode 100644
> > > > > > index 0000000000..68545941a9
> > > > > > --- /dev/null
> > > > > > +++ b/br-controller/br-flow-mgr.h
> > > > > > @@ -0,0 +1,65 @@
> > > > > > +/*
> > > > > > + * Licensed under the Apache License, Version 2.0 (the "License");
> > > > > > + * you may not use this file except in compliance with the License.
> > > > > > + * You may obtain a copy of the License at:
> > > > > > + *
> > > > > > + *     http://www.apache.org/licenses/LICENSE-2.0
> > > > > > + *
> > > > > > + * Unless required by applicable law or agreed to in writing, 
> > > > > > software
> > > > > > + * distributed under the License is distributed on an "AS IS" 
> > > > > > BASIS,
> > > > > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
> > > > > > implied.
> > > > > > + * See the License for the specific language governing permissions 
> > > > > > and
> > > > > > + * limitations under the License.
> > > > > > + */
> > > > > > +
> > > > > > +#ifndef BR_FLOW_MGR_H
> > > > > > +#define BR_FLOW_MGR_H 1
> > > > > > +
> > > > > > +#include <stdio.h>
> > > > > > +
> > > > > > +/* OVS includes. */
> > > > > > +#include "openvswitch/hmap.h"
> > > > > > +#include "openvswitch/list.h"
> > > > > > +
> > > > > > +/* OVN includes. */
> > > > > > +
> > > > > > +struct match;
> > > > > > +struct ofpbuf;
> > > > > > +struct uuid;
> > > > > > +
> > > > > > +#define DP_FLOW_TABLE_GLOBAL_KEY 0
> > > > > > +
> > > > > > +void br_flow_tables_init(void);
> > > > > > +void br_flow_tables_destroy(void);
> > > > > > +
> > > > > > +struct br_flow_table *br_flow_table_alloc(const char *bridge);
> > > > > > +struct br_flow_table *br_flow_table_get(const char *bridge);
> > > > > > +void br_flow_table_destroy(const char *bridge);
> > > > > > +
> > > > > > +void br_flow_switch_logical_oflow_tables(void);
> > > > > > +void br_flow_switch_logical_oflow_table(const char *bridge);
> > > > > > +void br_flow_switch_physical_oflow_tables(void);
> > > > > > +void br_flow_switch_physical_oflow_table(const char *bridge);
> > > > > > +
> > > > > > +void br_flow_add_logical_oflow(const char *bridge, uint8_t 
> > > > > > table_id,
> > > > > > +                               uint16_t priority, uint64_t cookie,
> > > > > > +                               const struct match *match,
> > > > > > +                               const struct ofpbuf *actions,
> > > > > > +                               const struct uuid *flow_uuid);
> > > > > > +void br_flow_add_physical_oflow(const char *bridge, uint8_t 
> > > > > > table_id,
> > > > > > +                                uint16_t priority, uint64_t cookie,
> > > > > > +                                const struct match *match,
> > > > > > +                                const struct ofpbuf *actions,
> > > > > > +                                const struct uuid *flow_uuid);
> > > > > > +
> > > > > > +void br_flow_remove_logical_oflows_all(const struct uuid 
> > > > > > *flow_uuid);
> > > > > > +void br_flow_remove_logical_oflows(const char *bridge,
> > > > > > +                                   const struct uuid *flow_uuid);
> > > > > > +void br_flow_remove_physical_oflows(const char *bridge,
> > > > > > +                                    const struct uuid *flow_uuid);
> > > > > > +void br_flow_flush_oflows(const char *bridge);
> > > > > > +void br_flow_flush_all_oflows(void);
> > > > > > +
> > > > > > +void br_flow_populate_oflow_msgs(const char *bridge, struct 
> > > > > > ovs_list *msgs);
> > > > > > +
> > > > > > +#endif /* BR_FLOW_MGR_H */
> > > > > > --
> > > > > > 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