On 10/26/23 20:15, [email protected] wrote:
> From: Numan Siddique <[email protected]>
>
> This new engine now maintains the load balancer and ACL data of a
> logical switch which was earlier part of northd engine node data.
> The main inputs to this engine are:
> - northd node
> - NB logical switch node
> - Port group node
>
> A record for each logical switch is maintained in the 'ls_lbacls'
> hmap table and this record stores the below data which was earlier
> part of 'struct ovn_datapath'.
>
> - bool has_stateful_acl;
> - bool has_lb_vip;
> - bool has_acls;
> - uint64_t max_acl_tier;
>
> This engine node becomes an input to 'lflow' node.
>
> Signed-off-by: Numan Siddique <[email protected]>
> ---
> lib/stopwatch-names.h | 1 +
> northd/automake.mk | 2 +
> northd/en-lflow.c | 4 +
> northd/en-lr-lb-nat-data.c | 8 +-
> northd/en-lr-lb-nat-data.h | 2 +
> northd/en-ls-lb-acls.c | 527 +++++++++++++++++++++++++++++++++++++
> northd/en-ls-lb-acls.h | 88 +++++++
> northd/en-port-group.h | 3 +
> northd/inc-proc-northd.c | 9 +
> northd/northd.c | 271 +++++++++----------
> northd/northd.h | 7 +-
> 11 files changed, 776 insertions(+), 146 deletions(-)
> create mode 100644 northd/en-ls-lb-acls.c
> create mode 100644 northd/en-ls-lb-acls.h
>
> diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h
> index 7d85acdaea..8b0018a593 100644
> --- a/lib/stopwatch-names.h
> +++ b/lib/stopwatch-names.h
> @@ -34,5 +34,6 @@
> #define SYNC_METERS_RUN_STOPWATCH_NAME "sync_meters_run"
> #define LR_NAT_RUN_STOPWATCH_NAME "lr_nat_run"
> #define LR_LB_NAT_DATA_RUN_STOPWATCH_NAME "lr_lb_nat_data"
> +#define LS_LBACLS_RUN_STOPWATCH_NAME "lr_lb_acls"
>
> #endif
> diff --git a/northd/automake.mk b/northd/automake.mk
> index 4116c487df..4593654726 100644
> --- a/northd/automake.mk
> +++ b/northd/automake.mk
> @@ -28,6 +28,8 @@ northd_ovn_northd_SOURCES = \
> northd/en-lr-nat.h \
> northd/en-lr-lb-nat-data.c \
> northd/en-lr-lb-nat-data.h \
> + northd/en-ls-lb-acls.c \
> + northd/en-ls-lb-acls.h \
> northd/inc-proc-northd.c \
> northd/inc-proc-northd.h \
> northd/ipam.c \
> diff --git a/northd/en-lflow.c b/northd/en-lflow.c
> index 229f4be1d0..648a477916 100644
> --- a/northd/en-lflow.c
> +++ b/northd/en-lflow.c
> @@ -21,6 +21,7 @@
> #include "en-lflow.h"
> #include "en-lr-nat.h"
> #include "en-lr-lb-nat-data.h"
> +#include "en-ls-lb-acls.h"
> #include "en-northd.h"
> #include "en-meters.h"
>
> @@ -44,6 +45,8 @@ lflow_get_input_data(struct engine_node *node,
> engine_get_input_data("sync_meters", node);
> struct ed_type_lr_lb_nat_data *lr_lb_nat_data =
> engine_get_input_data("lr_lb_nat_data", node);
> + struct ed_type_ls_lbacls *ls_lbacls_data =
> + engine_get_input_data("ls_lbacls", node);
>
> lflow_input->nbrec_bfd_table =
> EN_OVSDB_GET(engine_get_input("NB_bfd", node));
> @@ -67,6 +70,7 @@ lflow_get_input_data(struct engine_node *node,
> lflow_input->lr_ports = &northd_data->lr_ports;
> lflow_input->ls_port_groups = &pg_data->ls_port_groups;
> lflow_input->lr_lbnats = &lr_lb_nat_data->lr_lbnats;
> + lflow_input->ls_lbacls = &ls_lbacls_data->ls_lbacls;
> lflow_input->meter_groups = &sync_meters_data->meter_groups;
> lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
> lflow_input->svc_monitor_map = &northd_data->svc_monitor_map;
> diff --git a/northd/en-lr-lb-nat-data.c b/northd/en-lr-lb-nat-data.c
> index 19b638ce0b..d816d2321d 100644
> --- a/northd/en-lr-lb-nat-data.c
> +++ b/northd/en-lr-lb-nat-data.c
> @@ -299,9 +299,11 @@ lr_lb_nat_data_lb_data_handler(struct engine_node *node,
> void *data_)
> if (!hmapx_is_empty(&data->tracked_data.crupdated)) {
> struct hmapx_node *hmapx_node;
> /* For all the modified lr_lb_nat_data records (re)build the
> - * vip nats. */
> + * vip nats and re-evaluate 'has_lb_vip'. */
> HMAPX_FOR_EACH (hmapx_node, &data->tracked_data.crupdated) {
> - lr_lb_nat_data_build_vip_nats(hmapx_node->data);
> + lr_lbnat_rec = hmapx_node->data;
> + lr_lb_nat_data_build_vip_nats(lr_lbnat_rec);
> + lr_lbnat_rec->has_lb_vip = od_has_lb_vip(lr_lbnat_rec->od);
> }
>
> data->tracked = true;
> @@ -523,6 +525,8 @@ lr_lb_nat_data_record_init(struct lr_lb_nat_data_record
> *lr_lbnat_rec,
> if (!nbr->n_nat) {
> lr_lb_nat_data_build_vip_nats(lr_lbnat_rec);
> }
> +
> + lr_lbnat_rec->has_lb_vip = od_has_lb_vip(lr_lbnat_rec->od);
> }
>
> static struct lr_lb_nat_data_input
> diff --git a/northd/en-lr-lb-nat-data.h b/northd/en-lr-lb-nat-data.h
> index ffe41cad73..ac21d28a57 100644
> --- a/northd/en-lr-lb-nat-data.h
> +++ b/northd/en-lr-lb-nat-data.h
> @@ -39,6 +39,8 @@ struct lr_lb_nat_data_record {
> const struct ovn_datapath *od;
> const struct lr_nat_record *lrnat_rec;
>
> + bool has_lb_vip;
> +
> /* Load Balancer vIPs relevant for this datapath. */
> struct ovn_lb_ip_set *lb_ips;
>
> diff --git a/northd/en-ls-lb-acls.c b/northd/en-ls-lb-acls.c
> new file mode 100644
> index 0000000000..1ba7ecb3e2
> --- /dev/null
> +++ b/northd/en-ls-lb-acls.c
> @@ -0,0 +1,527 @@
> +/*
> + * Copyright (c) 2023, Red Hat, Inc.
> + *
> + * 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>
> +
> +#include <getopt.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +/* OVS includes */
> +#include "include/openvswitch/hmap.h"
> +#include "lib/bitmap.h"
> +#include "lib/socket-util.h"
> +#include "lib/uuidset.h"
> +#include "openvswitch/util.h"
> +#include "openvswitch/vlog.h"
> +#include "stopwatch.h"
> +
> +/* OVN includes */
> +#include "en-lb-data.h"
> +#include "en-ls-lb-acls.h"
> +#include "en-port-group.h"
> +#include "lib/inc-proc-eng.h"
> +#include "lib/lb.h"
> +#include "lib/ovn-nb-idl.h"
> +#include "lib/ovn-sb-idl.h"
> +#include "lib/ovn-util.h"
> +#include "lib/stopwatch-names.h"
> +#include "northd.h"
> +
> +VLOG_DEFINE_THIS_MODULE(en_ls_lbacls);
> +
> +/* Static function declarations. */
> +static void ls_lbacls_table_init(struct ls_lbacls_table *);
> +static void ls_lbacls_table_clear(struct ls_lbacls_table *);
> +static void ls_lbacls_table_destroy(struct ls_lbacls_table *);
> +static struct ls_lbacls_record *ls_lbacls_table_find_(
> + const struct ls_lbacls_table *, const struct nbrec_logical_switch *);
> +static void ls_lbacls_table_build(struct ls_lbacls_table *,
> + const struct ovn_datapaths *ls_datapaths,
> + const struct ls_port_group_table *);
> +
> +static struct ls_lbacls_input ls_lbacls_get_input_data(
> + struct engine_node *);
> +
> +static struct ls_lbacls_record *ls_lbacls_record_create(
> + struct ls_lbacls_table *,
> + const struct ovn_datapath *,
> + const struct ls_port_group_table *);
> +static void ls_lbacls_record_destroy(struct ls_lbacls_record *);
> +static void ls_lbacls_record_init(
> + struct ls_lbacls_record *,
> + const struct ovn_datapath *,
> + const struct ls_port_group *,
> + const struct ls_port_group_table *);
> +static void ls_lbacls_record_reinit(
> + struct ls_lbacls_record *,
> + const struct ls_port_group *,
> + const struct ls_port_group_table *);
> +static bool ls_has_lb_vip(const struct ovn_datapath *);
> +static void ls_lbacls_record_set_acl_flags(struct ls_lbacls_record *,
> + const struct ovn_datapath *,
> + const struct ls_port_group *,
> + const struct ls_port_group_table
> *);
> +static bool ls_lbacls_record_set_acl_flags_(struct ls_lbacls_record *,
> + struct nbrec_acl **,
> + size_t n_acls);
> +static struct ls_lbacls_input ls_lbacls_get_input_data(struct engine_node *);
> +static bool is_ls_acls_changed(const struct nbrec_logical_switch *);
> +static bool is_acls_seqno_changed(struct nbrec_acl **, size_t n_nb_acls);
> +
> +
> +/* public functions. */
> +const struct ls_lbacls_record *
> +ls_lbacls_table_find(
> + const struct ls_lbacls_table *table,
> + const struct nbrec_logical_switch *nbs)
> +{
> + return ls_lbacls_table_find_(table, nbs);
> +}
> +
> +void *
> +en_ls_lbacls_init(struct engine_node *node OVS_UNUSED,
> + struct engine_arg *arg OVS_UNUSED)
> +{
> + struct ed_type_ls_lbacls *data = xzalloc(sizeof *data);
> + ls_lbacls_table_init(&data->ls_lbacls);
> + hmapx_init(&data->tracked_data.crupdated);
> + hmapx_init(&data->tracked_data.deleted);
> + return data;
> +}
> +
> +void
> +en_ls_lbacls_cleanup(void *data_)
> +{
> + struct ed_type_ls_lbacls *data =
> + (struct ed_type_ls_lbacls *) data_;
> + ls_lbacls_table_destroy(&data->ls_lbacls);
> + hmapx_destroy(&data->tracked_data.crupdated);
> + hmapx_destroy(&data->tracked_data.deleted);
> +}
> +
> +void
> +en_ls_lbacls_clear_tracked_data(void *data_)
> +{
> + struct ed_type_ls_lbacls *data =
> + (struct ed_type_ls_lbacls *) data_;
> +
> + struct hmapx_node *hmapx_node;
> + HMAPX_FOR_EACH_SAFE (hmapx_node, &data->tracked_data.deleted) {
> + ls_lbacls_record_destroy(hmapx_node->data);
> + hmapx_delete(&data->tracked_data.deleted, hmapx_node);
> + }
> +
> + hmapx_clear(&data->tracked_data.crupdated);
> + data->tracked = false;
> +}
> +
> +void
> +en_ls_lbacls_run(struct engine_node *node, void *data_)
> +{
> + struct ls_lbacls_input input_data = ls_lbacls_get_input_data(node);
> + struct ed_type_ls_lbacls *data = data_;
> +
> + stopwatch_start(LS_LBACLS_RUN_STOPWATCH_NAME, time_msec());
> +
> + ls_lbacls_table_clear(&data->ls_lbacls);
> + ls_lbacls_table_build(&data->ls_lbacls, input_data.ls_datapaths,
> + input_data.ls_port_groups);
> +
> + data->tracked = false;
> + stopwatch_stop(LS_LBACLS_RUN_STOPWATCH_NAME, time_msec());
Same comment about missing stopwatch_start(LS_LBACLS_RUN_STOPWATCH_NAME,
...) as in the previous patch in this series.
Thanks,
Dumitru
> + engine_set_node_state(node, EN_UPDATED);
> +}
> +
> +/* Handler functions. */
> +bool
> +ls_lbacls_northd_handler(struct engine_node *node, void *data_)
> +{
> + struct northd_data *northd_data = engine_get_input_data("northd", node);
> + if (!northd_data->change_tracked) {
> + return false;
> + }
> +
> + struct northd_tracked_data *nd_changes =
> &northd_data->trk_northd_changes;
> + struct ls_lbacls_input input_data = ls_lbacls_get_input_data(node);
> + struct ls_lbacls_record *ls_lbacls_rec;
> + struct ed_type_ls_lbacls *data = data_;
> + const struct ovn_datapath *od;
> + struct hmapx_node *hmapx_node;
> +
> + HMAPX_FOR_EACH (hmapx_node, &nd_changes->ls_with_changed_lbs.crupdated) {
> + od = hmapx_node->data;
> +
> + ls_lbacls_rec = ls_lbacls_table_find_(&data->ls_lbacls, od->nbs);
> + if (!ls_lbacls_rec) {
> + ls_lbacls_rec = ls_lbacls_record_create(&data->ls_lbacls, od,
> +
> input_data.ls_port_groups);
> + } else {
> + ls_lbacls_record_reinit(ls_lbacls_rec, NULL,
> + input_data.ls_port_groups);
> + }
> +
> + /* Add the ls_lbacls_rec to the tracking data. */
> + hmapx_add(&data->tracked_data.crupdated, ls_lbacls_rec);
> + }
> +
> + if (!hmapx_is_empty(&data->tracked_data.crupdated)) {
> + data->tracked = true;
> + engine_set_node_state(node, EN_UPDATED);
> + }
> +
> + return true;
> +}
> +
> +bool
> +ls_lbacls_port_group_handler(struct engine_node *node, void *data_)
> +{
> + struct port_group_data *pg_data =
> + engine_get_input_data("port_group", node);
> +
> + if (pg_data->ls_port_groups_sets_changed) {
> + return false;
> + }
> +
> + /* port_group engine node doesn't provide the tracking data yet.
> + * Loop through all the ls port groups and update the ls_lbacls_rec.
> + * This is still better than returning false. */
> + struct ls_lbacls_input input_data = ls_lbacls_get_input_data(node);
> + struct ed_type_ls_lbacls *data = data_;
> + const struct ls_port_group *ls_pg;
> +
> + LS_PORT_GROUP_TABLE_FOR_EACH (ls_pg, input_data.ls_port_groups) {
> + struct ls_lbacls_record *ls_lbacls_rec =
> + ls_lbacls_table_find_(&data->ls_lbacls, ls_pg->nbs);
> +
> + bool modified = false;
> + if (!ls_lbacls_rec) {
> + const struct ovn_datapath *od;
> + od = ovn_datapath_find(&input_data.ls_datapaths->datapaths,
> + &ls_pg->nbs->header_.uuid);
> + ovs_assert(od);
> + ls_lbacls_rec = ls_lbacls_record_create(&data->ls_lbacls, od,
> + input_data.ls_port_groups);
> + modified = true;
> + } else {
> + bool had_stateful_acl = ls_lbacls_rec->has_stateful_acl;
> + uint64_t max_acl_tier = ls_lbacls_rec->max_acl_tier;
> + bool had_acls = ls_lbacls_rec->has_acls;
> +
> + ls_lbacls_record_reinit(ls_lbacls_rec, ls_pg,
> + input_data.ls_port_groups);
> +
> + if ((had_stateful_acl != ls_lbacls_rec->has_stateful_acl)
> + || (had_acls != ls_lbacls_rec->has_acls)
> + || max_acl_tier != ls_lbacls_rec->max_acl_tier) {
> + modified = true;
> + }
> + }
> +
> + if (modified) {
> + /* Add the ls_lbacls_rec to the tracking data. */
> + hmapx_add(&data->tracked_data.crupdated, ls_lbacls_rec);
> + }
> + }
> +
> + if (!hmapx_is_empty(&data->tracked_data.crupdated)) {
> + data->tracked = true;
> + engine_set_node_state(node, EN_UPDATED);
> + }
> +
> + return true;
> +}
> +
> +bool
> +ls_lbacls_logical_switch_handler(struct engine_node *node, void *data_)
> +{
> + struct ls_lbacls_input input_data = ls_lbacls_get_input_data(node);
> + const struct nbrec_logical_switch *nbs;
> + struct ed_type_ls_lbacls *data = data_;
> +
> + NBREC_LOGICAL_SWITCH_TABLE_FOR_EACH_TRACKED (nbs,
> + input_data.nbrec_logical_switch_table) {
> + if (!is_ls_acls_changed(nbs)) {
> + continue;
> + }
> +
> + struct ls_lbacls_record *ls_lbacls_rec =
> + ls_lbacls_table_find_(&data->ls_lbacls, nbs);
> +
> + if (nbrec_logical_switch_is_deleted(nbs)) {
> + if (ls_lbacls_rec) {
> + /* Remove the record from the entries. */
> + hmap_remove(&data->ls_lbacls.entries,
> + &ls_lbacls_rec->key_node);
> +
> + /* Add the ls_lbacls_rec to the tracking data. */
> + hmapx_add(&data->tracked_data.deleted, ls_lbacls_rec);
> + }
> + } else {
> + if (!ls_lbacls_rec) {
> + const struct ovn_datapath *od;
> + od = ovn_datapath_find(&input_data.ls_datapaths->datapaths,
> + &nbs->header_.uuid);
> + ovs_assert(od);
> + ls_lbacls_rec = ls_lbacls_record_create(&data->ls_lbacls, od,
> +
> input_data.ls_port_groups);
> + } else {
> + ls_lbacls_record_reinit(ls_lbacls_rec, NULL,
> + input_data.ls_port_groups);
> + }
> +
> + /* Add the ls_lbacls_rec to the tracking data. */
> + hmapx_add(&data->tracked_data.crupdated, ls_lbacls_rec);
> + }
> + }
> +
> + if (!hmapx_is_empty(&data->tracked_data.crupdated)
> + || !hmapx_is_empty(&data->tracked_data.deleted)) {
> + data->tracked = true;
> + engine_set_node_state(node, EN_UPDATED);
> + }
> +
> + return true;
> +}
> +
> +/* static functions. */
> +static void
> +ls_lbacls_table_init(struct ls_lbacls_table *table)
> +{
> + *table = (struct ls_lbacls_table) {
> + .entries = HMAP_INITIALIZER(&table->entries),
> + };
> +}
> +
> +static void
> +ls_lbacls_table_destroy(struct ls_lbacls_table *table)
> +{
> + ls_lbacls_table_clear(table);
> + hmap_destroy(&table->entries);
> +}
> +
> +static void
> +ls_lbacls_table_clear(struct ls_lbacls_table *table)
> +{
> + struct ls_lbacls_record *ls_lbacls_rec;
> + HMAP_FOR_EACH_POP (ls_lbacls_rec, key_node, &table->entries) {
> + ls_lbacls_record_destroy(ls_lbacls_rec);
> + }
> +}
> +
> +static void
> +ls_lbacls_table_build(struct ls_lbacls_table *table,
> + const struct ovn_datapaths *ls_datapaths,
> + const struct ls_port_group_table *ls_pgs)
> +{
> + const struct ovn_datapath *od;
> + HMAP_FOR_EACH (od, key_node, &ls_datapaths->datapaths) {
> + ls_lbacls_record_create(table, od, ls_pgs);
> + }
> +}
> +
> +struct ls_lbacls_record *
> +ls_lbacls_table_find_(const struct ls_lbacls_table *table,
> + const struct nbrec_logical_switch *nbs)
> +{
> + struct ls_lbacls_record *rec;
> +
> + HMAP_FOR_EACH_WITH_HASH (rec, key_node,
> + uuid_hash(&nbs->header_.uuid), &table->entries)
> {
> + if (nbs == rec->od->nbs) {
> + return rec;
> + }
> + }
> + return NULL;
> +}
> +
> +static struct ls_lbacls_record *
> +ls_lbacls_record_create(struct ls_lbacls_table *table,
> + const struct ovn_datapath *od,
> + const struct ls_port_group_table *ls_pgs)
> +{
> + struct ls_lbacls_record *ls_lbacls_rec = xzalloc(sizeof *ls_lbacls_rec);
> + ls_lbacls_rec->od = od;
> + ls_lbacls_record_init(ls_lbacls_rec, od, NULL, ls_pgs);
> +
> + hmap_insert(&table->entries, &ls_lbacls_rec->key_node,
> + uuid_hash(&ls_lbacls_rec->od->nbs->header_.uuid));
> +
> + return ls_lbacls_rec;
> +}
> +
> +static void
> +ls_lbacls_record_destroy(struct ls_lbacls_record *ls_lbacls_rec)
> +{
> + free(ls_lbacls_rec);
> +}
> +
> +static void
> +ls_lbacls_record_init(struct ls_lbacls_record *ls_lbacls_rec,
> + const struct ovn_datapath *od,
> + const struct ls_port_group *ls_pg,
> + const struct ls_port_group_table *ls_pgs)
> +{
> + ls_lbacls_rec->has_lb_vip = ls_has_lb_vip(od);
> + ls_lbacls_record_set_acl_flags(ls_lbacls_rec, od, ls_pg, ls_pgs);
> +}
> +
> +static void
> +ls_lbacls_record_reinit(struct ls_lbacls_record *ls_lbacls_rec,
> + const struct ls_port_group *ls_pg,
> + const struct ls_port_group_table *ls_pgs)
> +{
> + ls_lbacls_record_init(ls_lbacls_rec, ls_lbacls_rec->od, ls_pg, ls_pgs);
> +}
> +
> +static bool
> +lb_has_vip(const struct nbrec_load_balancer *lb)
> +{
> + return !smap_is_empty(&lb->vips);
> +}
> +
> +static bool
> +lb_group_has_vip(const struct nbrec_load_balancer_group *lb_group)
> +{
> + for (size_t i = 0; i < lb_group->n_load_balancer; i++) {
> + if (lb_has_vip(lb_group->load_balancer[i])) {
> + return true;
> + }
> + }
> + return false;
> +}
> +
> +static bool
> +ls_has_lb_vip(const struct ovn_datapath *od)
> +{
> + for (size_t i = 0; i < od->nbs->n_load_balancer; i++) {
> + if (lb_has_vip(od->nbs->load_balancer[i])) {
> + return true;
> + }
> + }
> +
> + for (size_t i = 0; i < od->nbs->n_load_balancer_group; i++) {
> + if (lb_group_has_vip(od->nbs->load_balancer_group[i])) {
> + return true;
> + }
> + }
> + return false;
> +}
> +
> +static void
> +ls_lbacls_record_set_acl_flags(struct ls_lbacls_record *ls_lbacls_rec,
> + const struct ovn_datapath *od,
> + const struct ls_port_group *ls_pg,
> + const struct ls_port_group_table *ls_pgs)
> +{
> + ls_lbacls_rec->has_stateful_acl = false;
> + ls_lbacls_rec->max_acl_tier = 0;
> + ls_lbacls_rec->has_acls = false;
> +
> + if (ls_lbacls_record_set_acl_flags_(ls_lbacls_rec, od->nbs->acls,
> + od->nbs->n_acls)) {
> + return;
> + }
> +
> + if (!ls_pg) {
> + ls_pg = ls_port_group_table_find(ls_pgs, od->nbs);
> + }
> +
> + if (!ls_pg) {
> + return;
> + }
> +
> + const struct ls_port_group_record *ls_pg_rec;
> + HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> + if (ls_lbacls_record_set_acl_flags_(ls_lbacls_rec,
> + ls_pg_rec->nb_pg->acls,
> + ls_pg_rec->nb_pg->n_acls)) {
> + return;
> + }
> + }
> +}
> +
> +static bool
> +ls_lbacls_record_set_acl_flags_(struct ls_lbacls_record *ls_lbacls_rec,
> + struct nbrec_acl **acls,
> + size_t n_acls)
> +{
> + /* A true return indicates that there are no possible ACL flags
> + * left to set on ls_lbacls record. A false return indicates that
> + * further ACLs should be explored in case more flags need to be
> + * set on ls_lbacls record.
> + */
> + if (!n_acls) {
> + return false;
> + }
> +
> + ls_lbacls_rec->has_acls = true;
> + for (size_t i = 0; i < n_acls; i++) {
> + const struct nbrec_acl *acl = acls[i];
> + if (acl->tier > ls_lbacls_rec->max_acl_tier) {
> + ls_lbacls_rec->max_acl_tier = acl->tier;
> + }
> + if (!ls_lbacls_rec->has_stateful_acl
> + && !strcmp(acl->action, "allow-related")) {
> + ls_lbacls_rec->has_stateful_acl = true;
> + }
> + if (ls_lbacls_rec->has_stateful_acl &&
> + ls_lbacls_rec->max_acl_tier ==
> + nbrec_acl_col_tier.type.value.integer.max) {
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static struct ls_lbacls_input
> +ls_lbacls_get_input_data(struct engine_node *node)
> +{
> + const struct northd_data *northd_data =
> + engine_get_input_data("northd", node);
> + const struct port_group_data *pg_data =
> + engine_get_input_data("port_group", node);
> +
> + return (struct ls_lbacls_input) {
> + .nbrec_logical_switch_table =
> + EN_OVSDB_GET(engine_get_input("NB_logical_switch", node)),
> + .ls_port_groups = &pg_data->ls_port_groups,
> + .ls_datapaths = &northd_data->ls_datapaths,
> + };
> +}
> +
> +static bool
> +is_acls_seqno_changed(struct nbrec_acl **nb_acls, size_t n_nb_acls)
> +{
> + for (size_t i = 0; i < n_nb_acls; i++) {
> + if (nbrec_acl_row_get_seqno(nb_acls[i],
> + OVSDB_IDL_CHANGE_MODIFY) > 0) {
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static bool
> +is_ls_acls_changed(const struct nbrec_logical_switch *nbs) {
> + return (nbrec_logical_switch_is_new(nbs)
> + || nbrec_logical_switch_is_deleted(nbs)
> + || nbrec_logical_switch_is_updated(nbs,
> + NBREC_LOGICAL_SWITCH_COL_ACLS)
> + || is_acls_seqno_changed(nbs->acls, nbs->n_acls));
> +}
> diff --git a/northd/en-ls-lb-acls.h b/northd/en-ls-lb-acls.h
> new file mode 100644
> index 0000000000..ccb75e40e8
> --- /dev/null
> +++ b/northd/en-ls-lb-acls.h
> @@ -0,0 +1,88 @@
> +/*
> + * Copyright (c) 2023, Red Hat, Inc.
> + *
> + * 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 EN_LS_LB_ACL_H
> +#define EN_LS_LB_ACL_H 1
> +
> +#include <stdint.h>
> +
> +/* OVS includes. */
> +#include "lib/hmapx.h"
> +#include "openvswitch/hmap.h"
> +#include "sset.h"
> +
> +/* OVN includes. */
> +#include "lib/inc-proc-eng.h"
> +#include "lib/lb.h"
> +#include "lib/ovn-nb-idl.h"
> +#include "lib/ovn-sb-idl.h"
> +#include "lib/ovn-util.h"
> +#include "lib/stopwatch-names.h"
> +
> +struct ls_lbacls_record {
> + struct hmap_node key_node;
> +
> + const struct ovn_datapath *od;
> + bool has_stateful_acl;
> + bool has_lb_vip;
> + bool has_acls;
> + uint64_t max_acl_tier;
> +};
> +
> +struct ls_lbacls_table {
> + struct hmap entries;
> +};
> +
> +#define LS_LBACLS_TABLE_FOR_EACH(LS_LBACLS_REC, TABLE) \
> + HMAP_FOR_EACH (LS_LBACLS_REC, key_node, &(TABLE)->entries)
> +
> +#define LS_LBACLS_TABLE_FOR_EACH_IN_P(LS_LBACLS_REC, JOBID, TABLE) \
> + HMAP_FOR_EACH_IN_PARALLEL (LS_LBACLS_REC, key_node, JOBID, \
> + &(TABLE)->entries)
> +
> +struct ls_lbacls_tracked_data {
> + /* Created or updated logical switch with LB and ACL data. */
> + struct hmapx crupdated; /* Stores 'struct ls_lbacls_record'. */
> +
> + /* Deleted logical switch with LB and ACL data. */
> + struct hmapx deleted; /* Stores 'struct ls_lbacls_record'. */
> +};
> +
> +struct ed_type_ls_lbacls {
> + struct ls_lbacls_table ls_lbacls;
> +
> + bool tracked;
> + struct ls_lbacls_tracked_data tracked_data;
> +};
> +
> +struct ls_lbacls_input {
> + const struct nbrec_logical_switch_table *nbrec_logical_switch_table;
> + const struct ls_port_group_table *ls_port_groups;
> + const struct ovn_datapaths *ls_datapaths;
> +};
> +
> +void *en_ls_lbacls_init(struct engine_node *, struct engine_arg *);
> +void en_ls_lbacls_cleanup(void *data);
> +void en_ls_lbacls_clear_tracked_data(void *data);
> +void en_ls_lbacls_run(struct engine_node *, void *data);
> +
> +bool ls_lbacls_northd_handler(struct engine_node *, void *data);
> +bool ls_lbacls_port_group_handler(struct engine_node *, void *data);
> +bool ls_lbacls_logical_switch_handler(struct engine_node *, void *data);
> +
> +const struct ls_lbacls_record *ls_lbacls_table_find(
> + const struct ls_lbacls_table *, const struct nbrec_logical_switch *);
> +
> +#endif /* EN_LS_LB_ACL_H */
> diff --git a/northd/en-port-group.h b/northd/en-port-group.h
> index 3b28a23694..54014062ce 100644
> --- a/northd/en-port-group.h
> +++ b/northd/en-port-group.h
> @@ -48,6 +48,9 @@ struct ls_port_group_record {
> struct sset ports; /* Subset of 'nb_pg' ports in this record. */
> };
>
> +#define LS_PORT_GROUP_TABLE_FOR_EACH(LS_PG, TABLE) \
> + HMAP_FOR_EACH (LS_PG, key_node, &(TABLE)->entries)
> +
> void ls_port_group_table_init(struct ls_port_group_table *);
> void ls_port_group_table_clear(struct ls_port_group_table *);
> void ls_port_group_table_destroy(struct ls_port_group_table *);
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 84627070a8..ab4af92aeb 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -33,6 +33,7 @@
> #include "en-lb-data.h"
> #include "en-lr-lb-nat-data.h"
> #include "en-lr-nat.h"
> +#include "en-ls-lb-acls.h"
> #include "en-northd.h"
> #include "en-lflow.h"
> #include "en-northd-output.h"
> @@ -150,6 +151,7 @@ static ENGINE_NODE(sync_to_sb_pb, "sync_to_sb_pb");
> static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
> static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_nat, "lr_nat");
> static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lr_lb_nat_data, "lr_lb_nat_data");
> +static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(ls_lbacls, "ls_lbacls");
>
> void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> struct ovsdb_idl_loop *sb)
> @@ -205,6 +207,12 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> engine_add_input(&en_lr_lb_nat_data, &en_lb_data,
> lr_lb_nat_data_lb_data_handler);
>
> + engine_add_input(&en_ls_lbacls, &en_northd, ls_lbacls_northd_handler);
> + engine_add_input(&en_ls_lbacls, &en_port_group,
> + ls_lbacls_port_group_handler);
> + engine_add_input(&en_ls_lbacls, &en_nb_logical_switch,
> + ls_lbacls_logical_switch_handler);
> +
> engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
> engine_add_input(&en_mac_binding_aging, &en_sb_mac_binding, NULL);
> engine_add_input(&en_mac_binding_aging, &en_northd, NULL);
> @@ -229,6 +237,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
> engine_add_input(&en_lflow, &en_port_group, lflow_port_group_handler);
> engine_add_input(&en_lflow, &en_lr_lb_nat_data, NULL);
> + engine_add_input(&en_lflow, &en_ls_lbacls, NULL);
>
> engine_add_input(&en_sync_to_sb_addr_set, &en_nb_address_set,
> sync_to_sb_addr_set_nb_address_set_handler);
> diff --git a/northd/northd.c b/northd/northd.c
> index c8a224d3cd..924f5cd7e0 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -45,6 +45,7 @@
> #include "en-lb-data.h"
> #include "en-lr-nat.h"
> #include "en-lr-lb-nat-data.h"
> +#include "en-ls-lb-acls.h"
> #include "lib/ovn-parallel-hmap.h"
> #include "ovn/actions.h"
> #include "ovn/features.h"
> @@ -575,7 +576,7 @@ lb_group_has_vip(const struct nbrec_load_balancer_group
> *lb_group)
> }
>
> static bool
> -ls_has_lb_vip(struct ovn_datapath *od)
> +ls_has_lb_vip(const struct ovn_datapath *od)
> {
> for (size_t i = 0; i < od->nbs->n_load_balancer; i++) {
> if (lb_has_vip(od->nbs->load_balancer[i])) {
> @@ -592,7 +593,7 @@ ls_has_lb_vip(struct ovn_datapath *od)
> }
>
> static bool
> -lr_has_lb_vip(struct ovn_datapath *od)
> +lr_has_lb_vip(const struct ovn_datapath *od)
> {
> for (size_t i = 0; i < od->nbr->n_load_balancer; i++) {
> if (lb_has_vip(od->nbr->load_balancer[i])) {
> @@ -608,13 +609,13 @@ lr_has_lb_vip(struct ovn_datapath *od)
> return false;
> }
>
> -static void
> -init_lb_for_datapath(struct ovn_datapath *od)
> +bool
> +od_has_lb_vip(const struct ovn_datapath *od)
> {
> if (od->nbs) {
> - od->has_lb_vip = ls_has_lb_vip(od);
> + return ls_has_lb_vip(od);
> } else {
> - od->has_lb_vip = lr_has_lb_vip(od);
> + return lr_has_lb_vip(od);
> }
> }
>
> @@ -1058,7 +1059,6 @@ join_datapaths(const struct nbrec_logical_switch_table
> *nbrec_ls_table,
>
> init_ipam_info_for_datapath(od);
> init_mcast_info_for_datapath(od);
> - init_lb_for_datapath(od);
> }
>
> const struct nbrec_logical_router *nbr;
> @@ -1089,7 +1089,6 @@ join_datapaths(const struct nbrec_logical_switch_table
> *nbrec_ls_table,
> ovs_list_push_back(nb_only, &od->list);
> }
> init_mcast_info_for_datapath(od);
> - init_lb_for_datapath(od);
> if (smap_get(&od->nbr->options, "chassis")) {
> od->is_gw_router = true;
> }
> @@ -2570,7 +2569,8 @@ get_nat_addresses(const struct ovn_port *op, size_t *n,
> bool routable_only,
> size_t n_nats = 0;
> struct eth_addr mac;
> if (!op || !op->nbrp || !op->od || !op->od->nbr
> - || (!op->od->nbr->n_nat && !op->od->has_lb_vip)
> + || (!op->od->nbr->n_nat && (!lr_lbnat_rec
> + || !lr_lbnat_rec->has_lb_vip))
> || !eth_addr_from_string(op->nbrp->mac, &mac)) {
> *n = n_nats;
> return NULL;
> @@ -3817,7 +3817,7 @@ build_lrouter_lbs_check(const struct ovn_datapaths
> *lr_datapaths)
> HMAP_FOR_EACH (od, key_node, &lr_datapaths->datapaths) {
> ovs_assert(od->nbr);
>
> - if (od->has_lb_vip && od->n_l3dgw_ports > 1
> + if (od_has_lb_vip(od) && od->n_l3dgw_ports > 1
> && !smap_get(&od->nbr->options, "chassis")) {
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> VLOG_WARN_RL(&rl, "Load-balancers are configured on logical "
> @@ -5441,7 +5441,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> BITMAP_FOR_EACH_1 (index, ods_size(ls_datapaths),
> lb_dps->nb_ls_map) {
> od = ls_datapaths->array[index];
> - init_lb_for_datapath(od);
>
> /* Add the ls datapath to the northd tracked data. */
> hmapx_add(&nd_changes->ls_with_changed_lbs.crupdated, od);
> @@ -5524,9 +5523,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> }
> }
>
> - /* Re-evaluate 'od->has_lb_vip' */
> - init_lb_for_datapath(od);
> -
> /* Add the ls datapath to the northd tracked data. */
> hmapx_add(&nd_changes->ls_with_changed_lbs.crupdated, od);
> }
> @@ -5564,9 +5560,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> }
> }
>
> - /* Re-evaluate 'od->has_lb_vip' */
> - init_lb_for_datapath(od);
> -
> /* Add the lr datapath to the northd tracked data. */
> hmapx_add(&nd_changes->lr_with_changed_lbs.crupdated, od);
> }
> @@ -5581,8 +5574,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> BITMAP_FOR_EACH_1 (index, ods_size(ls_datapaths),
> lb_dps->nb_ls_map) {
> od = ls_datapaths->array[index];
> - /* Re-evaluate 'od->has_lb_vip' */
> - init_lb_for_datapath(od);
>
> /* Add the ls datapath to the northd tracked data. */
> hmapx_add(&nd_changes->ls_with_changed_lbs.crupdated, od);
> @@ -5591,8 +5582,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> BITMAP_FOR_EACH_1 (index, ods_size(lr_datapaths),
> lb_dps->nb_lr_map) {
> od = lr_datapaths->array[index];
> - /* Re-evaluate 'od->has_lb_vip' */
> - init_lb_for_datapath(od);
>
> /* Add the lr datapath to the northd tracked data. */
> hmapx_add(&nd_changes->lr_with_changed_lbs.crupdated, od);
> @@ -5618,9 +5607,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> od = lbgrp_dps->lr[i];
> ovn_lb_datapaths_add_lr(lb_dps, 1, &od);
>
> - /* Re-evaluate 'od->has_lb_vip' */
> - init_lb_for_datapath(od);
> -
> /* Add the lr datapath to the northd tracked data. */
> hmapx_add(&nd_changes->lr_with_changed_lbs.crupdated, od);
> }
> @@ -5629,9 +5615,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data
> *trk_lb_data,
> od = lbgrp_dps->ls[i];
> ovn_lb_datapaths_add_ls(lb_dps, 1, &od);
>
> - /* Re-evaluate 'od->has_lb_vip' */
> - init_lb_for_datapath(od);
> -
> /* Add the ls datapath to the northd tracked data. */
> hmapx_add(&nd_changes->ls_with_changed_lbs.crupdated, od);
> }
> @@ -6573,63 +6556,6 @@ build_dhcpv6_action(struct ovn_port *op, struct
> in6_addr *offer_ip,
> return true;
> }
>
> -static bool
> -od_set_acl_flags(struct ovn_datapath *od, struct nbrec_acl **acls,
> - size_t n_acls)
> -{
> - /* A true return indicates that there are no possible ACL flags
> - * left to set on od. A false return indicates that further ACLs
> - * should be explored in case more flags need to be set on od
> - */
> - if (!n_acls) {
> - return false;
> - }
> -
> - od->has_acls = true;
> - for (size_t i = 0; i < n_acls; i++) {
> - const struct nbrec_acl *acl = acls[i];
> - if (acl->tier > od->max_acl_tier) {
> - od->max_acl_tier = acl->tier;
> - }
> - if (!od->has_stateful_acl && !strcmp(acl->action, "allow-related")) {
> - od->has_stateful_acl = true;
> - }
> - if (od->has_stateful_acl &&
> - od->max_acl_tier == nbrec_acl_col_tier.type.value.integer.max) {
> - return true;
> - }
> - }
> -
> - return false;
> -}
> -
> -static void
> -ls_get_acl_flags(struct ovn_datapath *od,
> - const struct ls_port_group_table *ls_port_groups)
> -{
> - od->has_acls = false;
> - od->has_stateful_acl = false;
> - od->max_acl_tier = 0;
> -
> - if (od_set_acl_flags(od, od->nbs->acls, od->nbs->n_acls)) {
> - return;
> - }
> -
> - const struct ls_port_group *ls_pg =
> - ls_port_group_table_find(ls_port_groups, od->nbs);
> - if (!ls_pg) {
> - return;
> - }
> -
> - const struct ls_port_group_record *ls_pg_rec;
> - HMAP_FOR_EACH (ls_pg_rec, key_node, &ls_pg->nb_pgs) {
> - if (od_set_acl_flags(od, ls_pg_rec->nb_pg->acls,
> - ls_pg_rec->nb_pg->n_acls)) {
> - return;
> - }
> - }
> -}
> -
> /* Adds the logical flows in the (in/out) check port sec stage only if
> * - the lport is disabled or
> * - lport is of type vtep - to skip the ingress pipeline.
> @@ -6774,9 +6700,10 @@ build_lswitch_output_port_sec_od(struct ovn_datapath
> *od,
> }
>
> static void
> -skip_port_from_conntrack(struct ovn_datapath *od, struct ovn_port *op,
> - enum ovn_stage in_stage, enum ovn_stage out_stage,
> - uint16_t priority, struct hmap *lflows)
> +skip_port_from_conntrack(const struct ovn_datapath *od, struct ovn_port *op,
> + bool has_stateful_acl, enum ovn_stage in_stage,
> + enum ovn_stage out_stage, uint16_t priority,
> + struct hmap *lflows)
> {
> /* Can't use ct() for router ports. Consider the following configuration:
> * lp1(10.0.0.2) on hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a
> @@ -6789,7 +6716,7 @@ skip_port_from_conntrack(struct ovn_datapath *od,
> struct ovn_port *op,
> * conntrack state across all chassis. */
>
> const char *ingress_action = "next;";
> - const char *egress_action = od->has_stateful_acl
> + const char *egress_action = has_stateful_acl
> ? "next;"
> : "ct_clear; next;";
>
> @@ -6808,7 +6735,7 @@ skip_port_from_conntrack(struct ovn_datapath *od,
> struct ovn_port *op,
> }
>
> static void
> -build_stateless_filter(struct ovn_datapath *od,
> +build_stateless_filter(const struct ovn_datapath *od,
> const struct nbrec_acl *acl,
> struct hmap *lflows)
> {
> @@ -6829,7 +6756,7 @@ build_stateless_filter(struct ovn_datapath *od,
> }
>
> static void
> -build_stateless_filters(struct ovn_datapath *od,
> +build_stateless_filters(const struct ovn_datapath *od,
> const struct ls_port_group_table *ls_port_groups,
> struct hmap *lflows)
> {
> @@ -6859,9 +6786,7 @@ build_stateless_filters(struct ovn_datapath *od,
> }
>
> static void
> -build_pre_acls(struct ovn_datapath *od,
> - const struct ls_port_group_table *ls_port_groups,
> - struct hmap *lflows)
> +build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
> {
> /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
> * allowed by default. */
> @@ -6873,18 +6798,26 @@ build_pre_acls(struct ovn_datapath *od,
>
> ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
> "eth.src == $svc_monitor_mac", "next;");
> +}
> +
> +static void
> +build_ls_lbacls_rec_pre_acls(const struct ls_lbacls_record *ls_lbacls_rec,
> + const struct ls_port_group_table
> *ls_port_groups,
> + struct hmap *lflows)
> +{
> + const struct ovn_datapath *od = ls_lbacls_rec->od;
>
> /* If there are any stateful ACL rules in this datapath, we may
> * send IP packets for some (allow) filters through the conntrack action,
> * which handles defragmentation, in order to match L4 headers. */
> - if (od->has_stateful_acl) {
> + if (ls_lbacls_rec->has_stateful_acl) {
> for (size_t i = 0; i < od->n_router_ports; i++) {
> - skip_port_from_conntrack(od, od->router_ports[i],
> + skip_port_from_conntrack(od, od->router_ports[i], true,
> S_SWITCH_IN_PRE_ACL,
> S_SWITCH_OUT_PRE_ACL,
> 110, lflows);
> }
> for (size_t i = 0; i < od->n_localnet_ports; i++) {
> - skip_port_from_conntrack(od, od->localnet_ports[i],
> + skip_port_from_conntrack(od, od->localnet_ports[i], true,
> S_SWITCH_IN_PRE_ACL,
> S_SWITCH_OUT_PRE_ACL,
> 110, lflows);
> @@ -6922,7 +6855,7 @@ build_pre_acls(struct ovn_datapath *od,
> REGBIT_CONNTRACK_DEFRAG" = 1; next;");
> ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 100, "ip",
> REGBIT_CONNTRACK_DEFRAG" = 1; next;");
> - } else if (od->has_lb_vip) {
> + } else if (ls_lbacls_rec->has_lb_vip) {
> /* We'll build stateless filters if there are LB rules so that
> * the stateless flows are not tracked in pre-lb. */
> build_stateless_filters(od, ls_port_groups, lflows);
> @@ -7050,30 +6983,40 @@ build_pre_lb(struct ovn_datapath *od, const struct
> shash *meter_groups,
> ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;");
> ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;");
>
> + /* Do not send statless flows via conntrack */
> + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
> + REGBIT_ACL_STATELESS" == 1", "next;");
> + ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
> + REGBIT_ACL_STATELESS" == 1", "next;");
> +}
> +
> +static void
> +build_ls_lbacls_rec_pre_lb(const struct ls_lbacls_record *ls_lbacls_rec,
> + struct hmap *lflows)
> +{
> + const struct ovn_datapath *od = ls_lbacls_rec->od;
> +
> for (size_t i = 0; i < od->n_router_ports; i++) {
> skip_port_from_conntrack(od, od->router_ports[i],
> + ls_lbacls_rec->has_stateful_acl,
> S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB,
> 110, lflows);
> }
> +
> /* Localnet ports have no need for going through conntrack, unless
> * the logical switch has a load balancer. Then, conntrack is necessary
> * so that traffic arriving via the localnet port can be load
> * balanced.
> */
> - if (!od->has_lb_vip) {
> + if (!ls_lbacls_rec->has_lb_vip) {
> for (size_t i = 0; i < od->n_localnet_ports; i++) {
> skip_port_from_conntrack(od, od->localnet_ports[i],
> + ls_lbacls_rec->has_stateful_acl,
> S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB,
> 110, lflows);
> }
> }
>
> - /* Do not sent statless flows via conntrack */
> - ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
> - REGBIT_ACL_STATELESS" == 1", "next;");
> - ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
> - REGBIT_ACL_STATELESS" == 1", "next;");
> -
> /* 'REGBIT_CONNTRACK_NAT' is set to let the pre-stateful table send
> * packet to conntrack for defragmentation and possibly for unNATting.
> *
> @@ -7104,7 +7047,7 @@ build_pre_lb(struct ovn_datapath *od, const struct
> shash *meter_groups,
> * ingress pipeline if a load balancer is configured. We can now
> * add a lflow to drop ct.inv packets.
> */
> - if (od->has_lb_vip) {
> + if (ls_lbacls_rec->has_lb_vip) {
> ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
> 100, "ip", REGBIT_CONNTRACK_NAT" = 1; next;");
> ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB,
> @@ -7145,10 +7088,12 @@ build_pre_stateful(struct ovn_datapath *od,
> }
>
> static void
> -build_acl_hints(struct ovn_datapath *od,
> +build_acl_hints(const struct ls_lbacls_record *ls_lbacls_rec,
> const struct chassis_features *features,
> struct hmap *lflows)
> {
> + const struct ovn_datapath *od = ls_lbacls_rec->od;
> +
> /* This stage builds hints for the IN/OUT_ACL stage. Based on various
> * combinations of ct flags packets may hit only a subset of the logical
> * flows in the IN/OUT_ACL stage.
> @@ -7172,13 +7117,13 @@ build_acl_hints(struct ovn_datapath *od,
> const char *match;
>
> /* In any case, advance to the next stage. */
> - if (!od->has_acls && !od->has_lb_vip) {
> + if (!ls_lbacls_rec->has_acls && !ls_lbacls_rec->has_lb_vip) {
> ovn_lflow_add(lflows, od, stage, UINT16_MAX, "1", "next;");
> } else {
> ovn_lflow_add(lflows, od, stage, 0, "1", "next;");
> }
>
> - if (!od->has_stateful_acl && !od->has_lb_vip) {
> + if (!ls_lbacls_rec->has_stateful_acl && !ls_lbacls_rec->has_lb_vip) {
> continue;
> }
>
> @@ -7314,10 +7259,10 @@ build_acl_log(struct ds *actions, const struct
> nbrec_acl *acl,
> }
>
> static void
> -consider_acl(struct hmap *lflows, struct ovn_datapath *od,
> +consider_acl(struct hmap *lflows, const struct ovn_datapath *od,
> const struct nbrec_acl *acl, bool has_stateful,
> bool ct_masked_mark, const struct shash *meter_groups,
> - struct ds *match, struct ds *actions)
> + uint64_t max_acl_tier, struct ds *match, struct ds *actions)
> {
> const char *ct_blocked_match = ct_masked_mark
> ? "ct_mark.blocked"
> @@ -7354,7 +7299,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath
> *od,
> /* All ACLS will start by matching on their respective tier. */
> size_t match_tier_len = 0;
> ds_clear(match);
> - if (od->max_acl_tier) {
> + if (max_acl_tier) {
> ds_put_format(match, REG_ACL_TIER " == %"PRId64" && ", acl->tier);
> match_tier_len = match->length;
> }
> @@ -7543,12 +7488,15 @@ ovn_update_ipv6_options(struct hmap *lr_ports)
> #define IPV6_CT_OMIT_MATCH "nd || nd_ra || nd_rs || mldv1 || mldv2"
>
> static void
> -build_acl_action_lflows(struct ovn_datapath *od, struct hmap *lflows,
> +build_acl_action_lflows(const struct ls_lbacls_record *ls_lbacls_rec,
> + struct hmap *lflows,
> const char *default_acl_action,
> const struct shash *meter_groups,
> struct ds *match,
> struct ds *actions)
> {
> + const struct ovn_datapath *od = ls_lbacls_rec->od;
> +
> enum ovn_stage stages [] = {
> S_SWITCH_IN_ACL_ACTION,
> S_SWITCH_IN_ACL_AFTER_LB_ACTION,
> @@ -7559,7 +7507,7 @@ build_acl_action_lflows(struct ovn_datapath *od, struct
> hmap *lflows,
> ds_put_cstr(actions, REGBIT_ACL_VERDICT_ALLOW " = 0; "
> REGBIT_ACL_VERDICT_DROP " = 0; "
> REGBIT_ACL_VERDICT_REJECT " = 0; ");
> - if (od->max_acl_tier) {
> + if (ls_lbacls_rec->max_acl_tier) {
> ds_put_cstr(actions, REG_ACL_TIER " = 0; ");
> }
>
> @@ -7567,7 +7515,7 @@ build_acl_action_lflows(struct ovn_datapath *od, struct
> hmap *lflows,
>
> for (size_t i = 0; i < ARRAY_SIZE(stages); i++) {
> enum ovn_stage stage = stages[i];
> - if (!od->has_acls) {
> + if (!ls_lbacls_rec->has_acls) {
> ovn_lflow_add(lflows, od, stage, 0, "1", "next;");
> continue;
> }
> @@ -7602,7 +7550,7 @@ build_acl_action_lflows(struct ovn_datapath *od, struct
> hmap *lflows,
> ovn_lflow_add(lflows, od, stage, 0, "1", ds_cstr(actions));
>
> struct ds tier_actions = DS_EMPTY_INITIALIZER;
> - for (size_t j = 0; j < od->max_acl_tier; j++) {
> + for (size_t j = 0; j < ls_lbacls_rec->max_acl_tier; j++) {
> ds_clear(match);
> ds_put_format(match, REG_ACL_TIER " == %"PRIuSIZE, j);
> ds_clear(&tier_actions);
> @@ -7618,7 +7566,7 @@ build_acl_action_lflows(struct ovn_datapath *od, struct
> hmap *lflows,
> }
>
> static void
> -build_acl_log_related_flows(struct ovn_datapath *od, struct hmap *lflows,
> +build_acl_log_related_flows(const struct ovn_datapath *od, struct hmap
> *lflows,
> const struct nbrec_acl *acl, bool has_stateful,
> bool ct_masked_mark,
> const struct shash *meter_groups,
> @@ -7691,15 +7639,19 @@ build_acl_log_related_flows(struct ovn_datapath *od,
> struct hmap *lflows,
> }
>
> static void
> -build_acls(struct ovn_datapath *od, const struct chassis_features *features,
> +build_acls(const struct ls_lbacls_record *ls_lbacls_rec,
> + const struct chassis_features *features,
> struct hmap *lflows,
> const struct ls_port_group_table *ls_port_groups,
> const struct shash *meter_groups)
> {
> + const struct ovn_datapath *od = ls_lbacls_rec->od;
> +
> const char *default_acl_action = default_acl_drop
> ? debug_implicit_drop_action()
> : "next;";
> - bool has_stateful = od->has_stateful_acl || od->has_lb_vip;
> + bool has_stateful = (ls_lbacls_rec->has_stateful_acl
> + || ls_lbacls_rec->has_lb_vip);
> const char *ct_blocked_match = features->ct_no_masked_label
> ? "ct_mark.blocked"
> : "ct_label.blocked";
> @@ -7713,8 +7665,8 @@ build_acls(struct ovn_datapath *od, const struct
> chassis_features *features,
> *
> * A related rule at priority 1 is added below if there
> * are any stateful ACLs in this datapath. */
> - if (!od->has_acls) {
> - if (!od->has_lb_vip) {
> + if (!ls_lbacls_rec->has_acls) {
> + if (!ls_lbacls_rec->has_lb_vip) {
> ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL_EVAL, UINT16_MAX, "1",
> "next;");
> ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL_EVAL, UINT16_MAX, "1",
> @@ -7877,7 +7829,8 @@ build_acls(struct ovn_datapath *od, const struct
> chassis_features *features,
> meter_groups, &match, &actions);
> consider_acl(lflows, od, acl, has_stateful,
> features->ct_no_masked_label,
> - meter_groups, &match, &actions);
> + meter_groups, ls_lbacls_rec->max_acl_tier,
> + &match, &actions);
> }
>
> const struct ls_port_group *ls_pg =
> @@ -7893,7 +7846,8 @@ build_acls(struct ovn_datapath *od, const struct
> chassis_features *features,
> meter_groups, &match, &actions);
> consider_acl(lflows, od, acl, has_stateful,
> features->ct_no_masked_label,
> - meter_groups, &match, &actions);
> + meter_groups, ls_lbacls_rec->max_acl_tier,
> + &match, &actions);
> }
> }
> }
> @@ -7911,7 +7865,7 @@ build_acls(struct ovn_datapath *od, const struct
> chassis_features *features,
> dns_actions);
> }
>
> - if (od->has_acls || od->has_lb_vip) {
> + if (ls_lbacls_rec->has_acls || ls_lbacls_rec->has_lb_vip) {
> /* Add a 34000 priority flow to advance the service monitor reply
> * packets to skip applying ingress ACLs. */
> ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL_EVAL, 34000,
> @@ -7925,8 +7879,8 @@ build_acls(struct ovn_datapath *od, const struct
> chassis_features *features,
> REGBIT_ACL_VERDICT_ALLOW" = 1; next;");
> }
>
> - build_acl_action_lflows(od, lflows, default_acl_action, meter_groups,
> - &match, &actions);
> + build_acl_action_lflows(ls_lbacls_rec, lflows, default_acl_action,
> + meter_groups, &match, &actions);
>
> ds_destroy(&match);
> ds_destroy(&actions);
> @@ -8571,8 +8525,11 @@ build_stateful(struct ovn_datapath *od,
> }
>
> static void
> -build_lb_hairpin(struct ovn_datapath *od, struct hmap *lflows)
> +build_lb_hairpin(const struct ls_lbacls_record *ls_lbacls_rec,
> + struct hmap *lflows)
> {
> + const struct ovn_datapath *od = ls_lbacls_rec->od;
> +
> /* Ingress Pre-Hairpin/Nat-Hairpin/Hairpin tabled (Priority 0).
> * Packets that don't need hairpinning should continue processing.
> */
> @@ -8580,7 +8537,7 @@ build_lb_hairpin(struct ovn_datapath *od, struct hmap
> *lflows)
> ovn_lflow_add(lflows, od, S_SWITCH_IN_NAT_HAIRPIN, 0, "1", "next;");
> ovn_lflow_add(lflows, od, S_SWITCH_IN_HAIRPIN, 0, "1", "next;");
>
> - if (od->has_lb_vip) {
> + if (ls_lbacls_rec->has_lb_vip) {
> /* Check if the packet needs to be hairpinned.
> * Set REGBIT_HAIRPIN in the original direction and
> * REGBIT_HAIRPIN_REPLY in the reply direction.
> @@ -9413,22 +9370,16 @@ build_lswitch_lflows_l2_unknown(struct ovn_datapath
> *od,
> static void
> build_lswitch_lflows_pre_acl_and_acl(
> struct ovn_datapath *od,
> - const struct ls_port_group_table *ls_port_groups,
> const struct chassis_features *features,
> struct hmap *lflows,
> const struct shash *meter_groups)
> {
> ovs_assert(od->nbs);
> - ls_get_acl_flags(od, ls_port_groups);
> -
> - build_pre_acls(od, ls_port_groups, lflows);
> + build_pre_acls(od, lflows);
> build_pre_lb(od, meter_groups, lflows);
> build_pre_stateful(od, features, lflows);
> - build_acl_hints(od, features, lflows);
> - build_acls(od, features, lflows, ls_port_groups, meter_groups);
> build_qos(od, lflows);
> build_stateful(od, features, lflows);
> - build_lb_hairpin(od, lflows);
> build_vtep_hairpin(od, lflows);
> }
>
> @@ -15487,7 +15438,7 @@ build_lrouter_nat_defrag_and_lb(
> * a dynamically negotiated FTP data channel), but will allow
> * related traffic such as an ICMP Port Unreachable through
> * that's generated from a non-listening UDP port. */
> - if (od->has_lb_vip && features->ct_lb_related) {
> + if (lr_lbnat_rec->has_lb_vip && features->ct_lb_related) {
> ds_clear(match);
>
> ds_put_cstr(match, "ct.rel && !ct.est && !ct.new");
> @@ -15512,7 +15463,7 @@ build_lrouter_nat_defrag_and_lb(
> * Pass the traffic that is already established to the next table with
> * proper flags set.
> */
> - if (od->has_lb_vip) {
> + if (lr_lbnat_rec->has_lb_vip) {
> ds_clear(match);
>
> ds_put_format(match, "ct.est && !ct.rel && !ct.new && %s.natted",
> @@ -15542,7 +15493,7 @@ build_lrouter_nat_defrag_and_lb(
> * not committed, it would produce ongoing datapath flows with the ct.new
> * flag set. Some NICs are unable to offload these flows.
> */
> - if (od->is_gw_router && (od->nbr->n_nat || od->has_lb_vip)) {
> + if (od->is_gw_router && (od->nbr->n_nat || lr_lbnat_rec->has_lb_vip)) {
> /* Do not send ND or ICMP packets to connection tracking. */
> ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
> "nd || nd_rs || nd_ra", "next;");
> @@ -15967,6 +15918,22 @@ build_lr_lbnat_data_flows(const struct
> lr_lb_nat_data_record *lr_lbnat_rec,
> meter_groups);
> }
>
> +static void
> +build_ls_lbacls_flows(const struct ls_lbacls_record *ls_lbacls_rec,
> + const struct ls_port_group_table *ls_pgs,
> + const struct chassis_features *features,
> + const struct shash *meter_groups,
> + struct hmap *lflows)
> +{
> + ovs_assert(ls_lbacls_rec->od);
> +
> + build_ls_lbacls_rec_pre_acls(ls_lbacls_rec, ls_pgs, lflows);
> + build_ls_lbacls_rec_pre_lb(ls_lbacls_rec, lflows);
> + build_acl_hints(ls_lbacls_rec, features, lflows);
> + build_acls(ls_lbacls_rec, features, lflows, ls_pgs, meter_groups);
> + build_lb_hairpin(ls_lbacls_rec, lflows);
> +}
> +
> struct lswitch_flow_build_info {
> const struct ovn_datapaths *ls_datapaths;
> const struct ovn_datapaths *lr_datapaths;
> @@ -15974,6 +15941,7 @@ struct lswitch_flow_build_info {
> const struct hmap *lr_ports;
> const struct ls_port_group_table *ls_port_groups;
> const struct lr_lb_nat_data_table *lr_lbnats;
> + const struct ls_lbacls_table *ls_lbacls;
> struct hmap *lflows;
> struct hmap *igmp_groups;
> const struct shash *meter_groups;
> @@ -15998,9 +15966,7 @@ build_lswitch_and_lrouter_iterate_by_ls(struct
> ovn_datapath *od,
> struct lswitch_flow_build_info *lsi)
> {
> ovs_assert(od->nbs);
> - build_lswitch_lflows_pre_acl_and_acl(od, lsi->ls_port_groups,
> - lsi->features,
> - lsi->lflows,
> + build_lswitch_lflows_pre_acl_and_acl(od, lsi->features, lsi->lflows,
> lsi->meter_groups);
>
> build_fwd_group_lflows(od, lsi->lflows);
> @@ -16115,6 +16081,7 @@ build_lflows_thread(void *arg)
> {
> struct worker_control *control = (struct worker_control *) arg;
> const struct lr_lb_nat_data_record *lr_lbnat_rec;
> + const struct ls_lbacls_record *ls_lbacls_rec;
> struct lswitch_flow_build_info *lsi;
> struct ovn_igmp_group *igmp_group;
> struct ovn_lb_datapaths *lb_dps;
> @@ -16243,6 +16210,19 @@ build_lflows_thread(void *arg)
> lsi->features);
> }
> }
> +
> + for (bnum = control->id;
> + bnum <= lsi->ls_lbacls->entries.mask;
> + bnum += control->pool->size)
> + {
> + LS_LBACLS_TABLE_FOR_EACH_IN_P (ls_lbacls_rec, bnum,
> + lsi->ls_lbacls) {
> + build_ls_lbacls_flows(ls_lbacls_rec, lsi->ls_port_groups,
> + lsi->features, lsi->meter_groups,
> + lsi->lflows);
> + }
> + }
> +
> for (bnum = control->id;
> bnum <= lsi->igmp_groups->mask;
> bnum += control->pool->size)
> @@ -16303,6 +16283,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
> const struct hmap *lr_ports,
> const struct ls_port_group_table *ls_pgs,
> const struct lr_lb_nat_data_table *lr_lbnats,
> + const struct ls_lbacls_table *ls_lbacls,
> struct hmap *lflows,
> struct hmap *igmp_groups,
> const struct shash *meter_groups,
> @@ -16333,6 +16314,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
> lsiv[index].lr_ports = lr_ports;
> lsiv[index].ls_port_groups = ls_pgs;
> lsiv[index].lr_lbnats = lr_lbnats;
> + lsiv[index].ls_lbacls = ls_lbacls;
> lsiv[index].igmp_groups = igmp_groups;
> lsiv[index].meter_groups = meter_groups;
> lsiv[index].lb_dps_map = lb_dps_map;
> @@ -16358,6 +16340,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
> free(lsiv);
> } else {
> const struct lr_lb_nat_data_record *lr_lbnat_rec;
> + const struct ls_lbacls_record *ls_lbacls_rec;
> struct ovn_igmp_group *igmp_group;
> struct ovn_lb_datapaths *lb_dps;
> struct ovn_datapath *od;
> @@ -16370,6 +16353,7 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
> .lr_ports = lr_ports,
> .ls_port_groups = ls_pgs,
> .lr_lbnats = lr_lbnats,
> + .ls_lbacls = ls_lbacls,
> .lflows = lflows,
> .igmp_groups = igmp_groups,
> .meter_groups = meter_groups,
> @@ -16439,6 +16423,12 @@ build_lswitch_and_lrouter_flows(const struct
> ovn_datapaths *ls_datapaths,
> lsi.features);
> }
>
> + LS_LBACLS_TABLE_FOR_EACH (ls_lbacls_rec, ls_lbacls) {
> + build_ls_lbacls_flows(ls_lbacls_rec, lsi.ls_port_groups,
> + lsi.features, lsi.meter_groups,
> + lsi.lflows);
> + }
> +
> stopwatch_start(LFLOWS_IGMP_STOPWATCH_NAME, time_msec());
> HMAP_FOR_EACH (igmp_group, hmap_node, igmp_groups) {
> build_lswitch_ip_mcast_igmp_mld(igmp_group,
> @@ -16535,6 +16525,7 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> input_data->lr_ports,
> input_data->ls_port_groups,
> input_data->lr_lbnats,
> + input_data->ls_lbacls,
> lflows,
> &igmp_groups,
> input_data->meter_groups,
> diff --git a/northd/northd.h b/northd/northd.h
> index 08a81b2c10..23b4754db4 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -89,6 +89,8 @@ ods_size(const struct ovn_datapaths *datapaths)
> return hmap_count(&datapaths->datapaths);
> }
>
> +bool od_has_lb_vip(const struct ovn_datapath *od);
> +
> struct tracked_ovn_ports {
> /* tracked created ports.
> * hmapx node data is 'struct ovn_port *' */
> @@ -179,6 +181,7 @@ struct lflow_input {
> const struct hmap *lr_ports;
> const struct ls_port_group_table *ls_port_groups;
> const struct lr_lb_nat_data_table *lr_lbnats;
> + const struct ls_lbacls_table *ls_lbacls;
> const struct shash *meter_groups;
> const struct hmap *lb_datapaths_map;
> const struct hmap *bfd_connections;
> @@ -288,11 +291,7 @@ struct ovn_datapath {
> struct hmap port_tnlids;
> uint32_t port_key_hint;
>
> - bool has_stateful_acl;
> - bool has_lb_vip;
> bool has_unknown;
> - bool has_acls;
> - uint64_t max_acl_tier;
> bool has_vtep_lports;
> bool has_arp_proxy_port;
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev