@@ -25,6 +25,7 @@
#define LFLOWS_PORTS_STOPWATCH_NAME "lflows_ports"
#define LFLOWS_LBS_STOPWATCH_NAME "lflows_lbs"
#define LFLOWS_LR_STATEFUL_STOPWATCH_NAME "lflows_lr_stateful"
+#define LFLOWS_LS_STATEFUL_STOPWATCH_NAME "lflows_ls_stateful"
#define LFLOWS_IGMP_STOPWATCH_NAME "lflows_igmp"
#define LFLOWS_DP_GROUPS_STOPWATCH_NAME "lflows_dp_groups"
#define LFLOWS_TO_SB_STOPWATCH_NAME "lflows_to_sb"
@@ -32,5 +33,6 @@
#define SYNC_METERS_RUN_STOPWATCH_NAME "sync_meters_run"
#define LR_NAT_RUN_STOPWATCH_NAME "lr_nat_run"
#define LR_STATEFUL_RUN_STOPWATCH_NAME "lr_stateful"
+#define LS_STATEFUL_RUN_STOPWATCH_NAME "ls_stateful"
#endif
@@ -28,6 +28,8 @@ northd_ovn_northd_SOURCES = \
northd/en-lr-nat.h \
northd/en-lr-stateful.c \
northd/en-lr-stateful.h \
+ northd/en-ls-stateful.c \
+ northd/en-ls-stateful.h \
northd/inc-proc-northd.c \
northd/inc-proc-northd.h \
northd/ipam.c \
@@ -21,6 +21,7 @@
#include "en-lflow.h"
#include "en-lr-nat.h"
#include "en-lr-stateful.h"
+#include "en-ls-stateful.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_stateful *lr_stateful_data =
engine_get_input_data("lr_stateful", node);
+ struct ed_type_ls_stateful *ls_stateful_data =
+ engine_get_input_data("ls_stateful", 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_stateful_table = &lr_stateful_data->table;
+ lflow_input->ls_stateful_table = &ls_stateful_data->table;
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;
@@ -311,7 +311,12 @@ lr_stateful_lb_data_handler(struct engine_node *node, void *data_)
struct hmapx_node *hmapx_node;
HMAPX_FOR_EACH (hmapx_node, &data->trk_data.crupdated) {
- lr_stateful_rebuild_vip_nats(hmapx_node->data);
+ struct lr_stateful_record *lr_stateful_rec = hmapx_node->data;
+ lr_stateful_rebuild_vip_nats(lr_stateful_rec);
+ const struct ovn_datapath *od =
+ ovn_datapaths_find_by_index(input_data.lr_datapaths,
+ lr_stateful_rec->lr_index);
+ lr_stateful_rec->has_lb_vip = od_has_lb_vip(od);
}
engine_set_node_state(node, EN_UPDATED);
@@ -510,11 +515,25 @@ lr_stateful_record_create(struct lr_stateful_table *table,
if (nbr->n_nat) {
lr_stateful_rebuild_vip_nats(lr_stateful_rec);
}
+ lr_stateful_rec->has_lb_vip = od_has_lb_vip(od);
hmap_insert(&table->entries, &lr_stateful_rec->key_node,
uuid_hash(&lr_stateful_rec->nbr_uuid));
table->array[od->index] = lr_stateful_rec;
+
+ /* Load balancers are not supported (yet) if a logical router has multiple
+ * distributed gateway port. Log a warning. */
+ if (lr_stateful_rec->has_lb_vip && lr_has_multiple_gw_ports(od)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_WARN_RL(&rl, "Load-balancers are configured on logical "
+ "router %s, which has %"PRIuSIZE" distributed "
+ "gateway ports. Load-balancer is not supported "
+ "yet when there is more than one distributed "
+ "gateway port on the router.",
+ od->nbr->name, od->n_l3dgw_ports);
+ }
+
return lr_stateful_rec;
}
@@ -56,6 +56,8 @@ struct lr_stateful_record {
/* This lrnat_rec comes from the en_lrnat engine node data. */
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;
@@ -113,4 +115,10 @@ lr_stateful_has_tracked_data(struct lr_stateful_tracked_data *trk_data) {
return !hmapx_is_empty(&trk_data->crupdated);
}
+static inline bool
+lr_stateful_rec_has_lb_vip(const struct lr_stateful_record *lr_stateful_rec)
+{
+ return lr_stateful_rec && lr_stateful_rec->has_lb_vip;
+}
+
#endif /* EN_lr_stateful_H */
new file mode 100644
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2024, 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-stateful.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_stateful);
+
+/* Static function declarations. */
+static void ls_stateful_table_init(struct ls_stateful_table *);
+static void ls_stateful_table_clear(struct ls_stateful_table *);
+static void ls_stateful_table_destroy(struct ls_stateful_table *);
+static struct ls_stateful_record *ls_stateful_table_find_(
+ const struct ls_stateful_table *, const struct nbrec_logical_switch *);
+static void ls_stateful_table_build(struct ls_stateful_table *,
+ const struct ovn_datapaths *ls_datapaths,
+ const struct ls_port_group_table *);
+
+static struct ls_stateful_input ls_stateful_get_input_data(
+ struct engine_node *);
+
+static struct ls_stateful_record *ls_stateful_record_create(
+ struct ls_stateful_table *,
+ const struct ovn_datapath *,
+ const struct ls_port_group_table *);
+static void ls_stateful_record_destroy(struct ls_stateful_record *);
+static void ls_stateful_record_init(
+ struct ls_stateful_record *,
+ const struct ovn_datapath *,
+ const struct ls_port_group *,
+ const struct ls_port_group_table *);
+static void ls_stateful_record_reinit(
+ struct ls_stateful_record *,
+ const struct ovn_datapath *,
+ const struct ls_port_group *,
+ const struct ls_port_group_table *);
+static bool ls_has_lb_vip(const struct ovn_datapath *);
+static void ls_stateful_record_set_acl_flags(
+ struct ls_stateful_record *, const struct ovn_datapath *,
+ const struct ls_port_group *, const struct ls_port_group_table *);
+static bool ls_stateful_record_set_acl_flags_(struct ls_stateful_record *,
+ struct nbrec_acl **,
+ size_t n_acls);
+static struct ls_stateful_input ls_stateful_get_input_data(
+ struct engine_node *);
+
+struct ls_stateful_input {
+ const struct ls_port_group_table *ls_port_groups;
+ const struct ovn_datapaths *ls_datapaths;
+};
+
+/* public functions. */
+void *
+en_ls_stateful_init(struct engine_node *node OVS_UNUSED,
+ struct engine_arg *arg OVS_UNUSED)
+{
+ struct ed_type_ls_stateful *data = xzalloc(sizeof *data);
+ ls_stateful_table_init(&data->table);
+ hmapx_init(&data->trk_data.crupdated);
+ return data;
+}
+
+void
+en_ls_stateful_cleanup(void *data_)
+{
+ struct ed_type_ls_stateful *data = data_;
+ ls_stateful_table_destroy(&data->table);
+ hmapx_destroy(&data->trk_data.crupdated);
+}
+
+void
+en_ls_stateful_clear_tracked_data(void *data_)
+{
+ struct ed_type_ls_stateful *data = data_;
+ hmapx_clear(&data->trk_data.crupdated);
+}
+
+void
+en_ls_stateful_run(struct engine_node *node, void *data_)
+{
+ struct ls_stateful_input input_data = ls_stateful_get_input_data(node);
+ struct ed_type_ls_stateful *data = data_;
+
+ stopwatch_start(LS_STATEFUL_RUN_STOPWATCH_NAME, time_msec());
+
+ ls_stateful_table_clear(&data->table);
+ ls_stateful_table_build(&data->table, input_data.ls_datapaths,
+ input_data.ls_port_groups);
+
+ stopwatch_stop(LS_STATEFUL_RUN_STOPWATCH_NAME, time_msec());
+ engine_set_node_state(node, EN_UPDATED);
+}
+
+/* Handler functions. */
+bool
+ls_stateful_northd_handler(struct engine_node *node, void *data_)
+{
+ struct northd_data *northd_data = engine_get_input_data("northd", node);
+ if (!northd_has_tracked_data(&northd_data->trk_data)) {
+ return false;
+ }
+
+ if (!northd_has_ls_lbs_in_tracked_data(&northd_data->trk_data) &&
+ !northd_has_ls_acls_in_tracked_data(&northd_data->trk_data)) {
+ return true;
+ }
+
+ struct northd_tracked_data *nd_changes = &northd_data->trk_data;
+ struct ls_stateful_input input_data = ls_stateful_get_input_data(node);
+ struct ed_type_ls_stateful *data = data_;
+ struct hmapx_node *hmapx_node;
+
+ struct hmapx changed_stateful_od = HMAPX_INITIALIZER(&changed_stateful_od);
+ HMAPX_FOR_EACH (hmapx_node, &nd_changes->ls_with_changed_lbs) {
+ hmapx_add(&changed_stateful_od, hmapx_node->data);
+ }
+
+ HMAPX_FOR_EACH (hmapx_node, &nd_changes->ls_with_changed_acls) {
+ hmapx_add(&changed_stateful_od, hmapx_node->data);
+ }
+
+ HMAPX_FOR_EACH (hmapx_node, &changed_stateful_od) {
+ const struct ovn_datapath *od = hmapx_node->data;
+
+ struct ls_stateful_record *ls_stateful_rec = ls_stateful_table_find_(
+ &data->table, od->nbs);
+ ovs_assert(ls_stateful_rec);
+ ls_stateful_record_reinit(ls_stateful_rec, od, NULL,
+ input_data.ls_port_groups);
+
+ /* Add the ls_stateful_rec to the tracking data. */
+ hmapx_add(&data->trk_data.crupdated, ls_stateful_rec);
+ }
+
+ if (ls_stateful_has_tracked_data(&data->trk_data)) {
+ engine_set_node_state(node, EN_UPDATED);
+ }
+
+ hmapx_destroy(&changed_stateful_od);
+
+ return true;
+}
+
+bool
+ls_stateful_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_stateful_rec.
+ * This is still better than returning false. */
+ struct ls_stateful_input input_data = ls_stateful_get_input_data(node);
+ struct ed_type_ls_stateful *data = data_;
+ const struct ls_port_group *ls_pg;
+
+ LS_PORT_GROUP_TABLE_FOR_EACH (ls_pg, input_data.ls_port_groups) {
+ struct ls_stateful_record *ls_stateful_rec =
+ ls_stateful_table_find_(&data->table, ls_pg->nbs);
+ ovs_assert(ls_stateful_rec);
+ const struct ovn_datapath *od =
+ ovn_datapaths_find_by_index(input_data.ls_datapaths,
+ ls_stateful_rec->ls_index);
+ bool had_stateful_acl = ls_stateful_rec->has_stateful_acl;
+ uint64_t max_acl_tier = ls_stateful_rec->max_acl_tier;
+ bool had_acls = ls_stateful_rec->has_acls;
+ bool modified = false;
+
+ ls_stateful_record_reinit(ls_stateful_rec, od, ls_pg,
+ input_data.ls_port_groups);
+
+ if ((had_stateful_acl != ls_stateful_rec->has_stateful_acl)
+ || (had_acls != ls_stateful_rec->has_acls)
+ || max_acl_tier != ls_stateful_rec->max_acl_tier) {
+ modified = true;
+ }
+
+ if (modified) {
+ /* Add the ls_stateful_rec to the tracking data. */
+ hmapx_add(&data->trk_data.crupdated, ls_stateful_rec);
+ }
+ }
+
+ if (ls_stateful_has_tracked_data(&data->trk_data)) {
+ engine_set_node_state(node, EN_UPDATED);
+ }
+ return true;
+}
+
+/* static functions. */
+static void
+ls_stateful_table_init(struct ls_stateful_table *table)
+{
+ *table = (struct ls_stateful_table) {
+ .entries = HMAP_INITIALIZER(&table->entries),
+ };
+}
+
+static void
+ls_stateful_table_destroy(struct ls_stateful_table *table)
+{
+ ls_stateful_table_clear(table);
+ hmap_destroy(&table->entries);
+}
+
+static void
+ls_stateful_table_clear(struct ls_stateful_table *table)
+{
+ struct ls_stateful_record *ls_stateful_rec;
+ HMAP_FOR_EACH_POP (ls_stateful_rec, key_node, &table->entries) {
+ ls_stateful_record_destroy(ls_stateful_rec);
+ }
+}
+
+static void
+ls_stateful_table_build(struct ls_stateful_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_stateful_record_create(table, od, ls_pgs);
+ }
+}
+
+static struct ls_stateful_record *
+ls_stateful_table_find_(const struct ls_stateful_table *table,
+ const struct nbrec_logical_switch *nbs)
+{
+ struct ls_stateful_record *ls_stateful_rec;
+
+ HMAP_FOR_EACH_WITH_HASH (ls_stateful_rec, key_node,
+ uuid_hash(&nbs->header_.uuid), &table->entries) {
+ if (uuid_equals(&ls_stateful_rec->nbs_uuid, &nbs->header_.uuid)) {
+ return ls_stateful_rec;
+ }
+ }
+ return NULL;
+}
+
+static struct ls_stateful_record *
+ls_stateful_record_create(struct ls_stateful_table *table,
+ const struct ovn_datapath *od,
+ const struct ls_port_group_table *ls_pgs)
+{
+ struct ls_stateful_record *ls_stateful_rec =
+ xzalloc(sizeof *ls_stateful_rec);
+ ls_stateful_rec->ls_index = od->index;
+ ls_stateful_rec->nbs_uuid = od->nbs->header_.uuid;
+ ls_stateful_record_init(ls_stateful_rec, od, NULL, ls_pgs);
+
+ hmap_insert(&table->entries, &ls_stateful_rec->key_node,
+ uuid_hash(&od->nbs->header_.uuid));
+
+ return ls_stateful_rec;
+}
+
+static void
+ls_stateful_record_destroy(struct ls_stateful_record *ls_stateful_rec)
+{
+ free(ls_stateful_rec);
+}
+
+static void
+ls_stateful_record_init(struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ const struct ls_port_group *ls_pg,
+ const struct ls_port_group_table *ls_pgs)
+{
+ ls_stateful_rec->has_lb_vip = ls_has_lb_vip(od);
+ ls_stateful_record_set_acl_flags(ls_stateful_rec, od, ls_pg, ls_pgs);
+}
+
+static void
+ls_stateful_record_reinit(struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ const struct ls_port_group *ls_pg,
+ const struct ls_port_group_table *ls_pgs)
+{
+ ls_stateful_record_init(ls_stateful_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_stateful_record_set_acl_flags(struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ const struct ls_port_group *ls_pg,
+ const struct ls_port_group_table *ls_pgs)
+{
+ ls_stateful_rec->has_stateful_acl = false;
+ ls_stateful_rec->max_acl_tier = 0;
+ ls_stateful_rec->has_acls = false;
+
+ if (ls_stateful_record_set_acl_flags_(ls_stateful_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_stateful_record_set_acl_flags_(ls_stateful_rec,
+ ls_pg_rec->nb_pg->acls,
+ ls_pg_rec->nb_pg->n_acls)) {
+ return;
+ }
+ }
+}
+
+static bool
+ls_stateful_record_set_acl_flags_(struct ls_stateful_record *ls_stateful_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_stateful record. A false return indicates that
+ * further ACLs should be explored in case more flags need to be
+ * set on ls_stateful record.
+ */
+ if (!n_acls) {
+ return false;
+ }
+
+ ls_stateful_rec->has_acls = true;
+ for (size_t i = 0; i < n_acls; i++) {
+ const struct nbrec_acl *acl = acls[i];
+ if (acl->tier > ls_stateful_rec->max_acl_tier) {
+ ls_stateful_rec->max_acl_tier = acl->tier;
+ }
+ if (!ls_stateful_rec->has_stateful_acl
+ && !strcmp(acl->action, "allow-related")) {
+ ls_stateful_rec->has_stateful_acl = true;
+ }
+ if (ls_stateful_rec->has_stateful_acl &&
+ ls_stateful_rec->max_acl_tier ==
+ nbrec_acl_col_tier.type.value.integer.max) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static struct ls_stateful_input
+ls_stateful_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_stateful_input) {
+ .ls_port_groups = &pg_data->ls_port_groups,
+ .ls_datapaths = &northd_data->ls_datapaths,
+ };
+}
new file mode 100644
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024, 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_STATEFUL_H
+#define EN_LS_STATEFUL_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_stateful_record {
+ struct hmap_node key_node;
+
+ /* UUID of the NB Logical switch. */
+ struct uuid nbs_uuid;
+
+ /* Unique id of the logical switch. Note : This id is assigned
+ * by the northd engine node for each logical switch. */
+ size_t ls_index;
+
+ bool has_stateful_acl;
+ bool has_lb_vip;
+ bool has_acls;
+ uint64_t max_acl_tier;
+};
+
+struct ls_stateful_table {
+ struct hmap entries;
+};
+
+#define LS_STATEFUL_TABLE_FOR_EACH(LS_STATEFUL_REC, TABLE) \
+ HMAP_FOR_EACH (LS_STATEFUL_REC, key_node, &(TABLE)->entries)
+
+#define LS_STATEFUL_TABLE_FOR_EACH_IN_P(LS_STATEFUL_REC, JOBID, TABLE) \
+ HMAP_FOR_EACH_IN_PARALLEL (LS_STATEFUL_REC, key_node, JOBID, \
+ &(TABLE)->entries)
+
+struct ls_stateful_tracked_data {
+ /* Created or updated logical switch with LB and ACL data. */
+ struct hmapx crupdated; /* Stores 'struct ls_stateful_record'. */
+};
+
+struct ed_type_ls_stateful {
+ struct ls_stateful_table table;
+ struct ls_stateful_tracked_data trk_data;
+};
+
+void *en_ls_stateful_init(struct engine_node *, struct engine_arg *);
+void en_ls_stateful_cleanup(void *data);
+void en_ls_stateful_clear_tracked_data(void *data);
+void en_ls_stateful_run(struct engine_node *, void *data);
+
+bool ls_stateful_northd_handler(struct engine_node *, void *data);
+bool ls_stateful_port_group_handler(struct engine_node *, void *data);
+
+const struct ls_stateful_record *ls_stateful_table_find(
+ const struct ls_stateful_table *, const struct nbrec_logical_switch *);
+
+static inline bool
+ls_stateful_has_tracked_data(struct ls_stateful_tracked_data *trk_data) {
+ return !hmapx_is_empty(&trk_data->crupdated);
+}
+
+#endif /* EN_LS_STATEFUL_H */
@@ -171,7 +171,7 @@ northd_nb_logical_switch_handler(struct engine_node *node,
return false;
}
- if (northd_has_lsps_in_tracked_data(&nd->trk_data)) {
+ if (northd_has_tracked_data(&nd->trk_data)) {
engine_set_node_state(node, EN_UPDATED);
}
@@ -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 *);
@@ -33,6 +33,7 @@
#include "en-lb-data.h"
#include "en-lr-stateful.h"
#include "en-lr-nat.h"
+#include "en-ls-stateful.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_stateful, "lr_stateful");
+static ENGINE_NODE_WITH_CLEAR_TRACK_DATA(ls_stateful, "ls_stateful");
void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
struct ovsdb_idl_loop *sb)
@@ -200,6 +202,10 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
engine_add_input(&en_lr_stateful, &en_lb_data,
lr_stateful_lb_data_handler);
+ engine_add_input(&en_ls_stateful, &en_northd, ls_stateful_northd_handler);
+ engine_add_input(&en_ls_stateful, &en_port_group,
+ ls_stateful_port_group_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);
@@ -222,6 +228,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
engine_add_input(&en_lflow, &en_sb_multicast_group, NULL);
engine_add_input(&en_lflow, &en_sb_igmp_group, NULL);
engine_add_input(&en_lflow, &en_lr_stateful, NULL);
+ engine_add_input(&en_lflow, &en_ls_stateful, NULL);
engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
engine_add_input(&en_lflow, &en_port_group, lflow_port_group_handler);
@@ -46,6 +46,7 @@
#include "en-lb-data.h"
#include "en-lr-nat.h"
#include "en-lr-stateful.h"
+#include "en-ls-stateful.h"
#include "lib/ovn-parallel-hmap.h"
#include "ovn/actions.h"
#include "ovn/features.h"
@@ -576,7 +577,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])) {
@@ -593,7 +594,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])) {
@@ -609,13 +610,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);
}
}
@@ -1065,7 +1066,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;
@@ -1096,7 +1096,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;
}
@@ -2583,7 +2582,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_stateful_rec_has_lb_vip(lr_stateful_rec))
|| !eth_addr_from_string(op->nbrp->mac, &mac)) {
*n = n_nats;
return NULL;
@@ -3822,27 +3822,6 @@ build_lb_svcs(
}
}
-static void
-build_lrouter_lbs_check(const struct ovn_datapaths *lr_datapaths)
-{
- struct ovn_datapath *od;
-
- HMAP_FOR_EACH (od, key_node, &lr_datapaths->datapaths) {
- ovs_assert(od->nbr);
-
- if (od->has_lb_vip && 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 "
- "router %s, which has %"PRIuSIZE" distributed "
- "gateway ports. Load-balancer is not supported "
- "yet when there is more than one distributed "
- "gateway port on the router.",
- od->nbr->name, od->n_l3dgw_ports);
- }
- }
-}
-
static void
build_lswitch_lbs_from_lrouter(struct ovn_datapaths *lr_datapaths,
struct hmap *lb_dps_map,
@@ -3905,7 +3884,6 @@ build_lb_port_related_data(
struct sset *svc_monitor_lsps,
struct hmap *svc_monitor_map)
{
- build_lrouter_lbs_check(lr_datapaths);
build_lb_svcs(ovnsb_txn, sbrec_service_monitor_table, ls_ports, lb_dps_map,
svc_monitor_lsps, svc_monitor_map);
build_lswitch_lbs_from_lrouter(lr_datapaths, lb_dps_map, lb_group_dps_map);
@@ -4746,6 +4724,8 @@ destroy_northd_data_tracked_changes(struct northd_data *nd)
destroy_tracked_ovn_ports(&trk_changes->trk_lsps);
destroy_tracked_lbs(&trk_changes->trk_lbs);
hmapx_clear(&trk_changes->trk_nat_lrs);
+ hmapx_clear(&trk_changes->ls_with_changed_lbs);
+ hmapx_clear(&trk_changes->ls_with_changed_acls);
trk_changes->type = NORTHD_TRACKED_NONE;
}
@@ -4760,6 +4740,8 @@ init_northd_tracked_data(struct northd_data *nd)
hmapx_init(&trk_data->trk_lbs.crupdated);
hmapx_init(&trk_data->trk_lbs.deleted);
hmapx_init(&trk_data->trk_nat_lrs);
+ hmapx_init(&trk_data->ls_with_changed_lbs);
+ hmapx_init(&trk_data->ls_with_changed_acls);
}
static void
@@ -4773,6 +4755,8 @@ destroy_northd_tracked_data(struct northd_data *nd)
hmapx_destroy(&trk_data->trk_lbs.crupdated);
hmapx_destroy(&trk_data->trk_lbs.deleted);
hmapx_destroy(&trk_data->trk_nat_lrs);
+ hmapx_destroy(&trk_data->ls_with_changed_lbs);
+ hmapx_destroy(&trk_data->ls_with_changed_acls);
}
/* Check if a changed LSP can be handled incrementally within the I-P engine
@@ -4888,6 +4872,7 @@ ls_port_create(struct ovsdb_idl_txn *ovnsb_txn, struct hmap *ls_ports,
* - logical switch ports.
* - load balancers.
* - load balancer groups.
+ * - ACLs
*/
static bool
ls_changes_can_be_handled(
@@ -5112,6 +5097,25 @@ fail:
return false;
}
+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_updated(nbs, NBREC_LOGICAL_SWITCH_COL_ACLS)
+ || is_acls_seqno_changed(nbs->acls, nbs->n_acls));
+}
+
/* Return true if changes are handled incrementally, false otherwise.
* When there are any changes, try to track what's exactly changed and set
* northd_data->trk_data accordingly.
@@ -5153,6 +5157,10 @@ northd_handle_ls_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
ni, nd, od, &trk_data->trk_lsps)) {
goto fail;
}
+
+ if (is_ls_acls_changed(changed_ls)) {
+ hmapx_add(&trk_data->ls_with_changed_acls, od);
+ }
}
if (!hmapx_is_empty(&trk_data->trk_lsps.created)
@@ -5161,6 +5169,10 @@ northd_handle_ls_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
trk_data->type |= NORTHD_TRACKED_PORTS;
}
+ if (!hmapx_is_empty(&trk_data->ls_with_changed_acls)) {
+ trk_data->type |= NORTHD_TRACKED_LS_ACLS;
+ }
+
return true;
fail:
@@ -5382,10 +5394,6 @@ northd_handle_sb_port_binding_changes(
* due to association of a load balancer (eg. ovn-nbctl ls-lb-add sw0 lb1),
* the logical switch datapath is added to the load balancer (represented
* by 'struct ovn_lb_datapaths') by calling ovn_lb_datapaths_add_ls().
- *
- * - For every 'lb' in the tracked data (trk_lb_data->crupdated_lbs) ,
- * it gets the associated logical switches and for each switch it
- * re-evaluates 'od->has_lb_vip' to reflect any changes to the lb vips.
* */
bool
northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
@@ -5446,13 +5454,13 @@ northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
lb_dps = ovn_lb_datapaths_find(lb_datapaths_map, lb_uuid);
ovs_assert(lb_dps);
- /* Re-evaluate 'od->has_lb_vip for od's associated with the
- * deleted lb. */
size_t index;
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, od);
}
hmap_remove(lb_datapaths_map, &lb_dps->hmap_node);
@@ -5532,8 +5540,8 @@ 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, od);
}
LIST_FOR_EACH (codlb, list_node, &trk_lb_data->crupdated_lr_lbs) {
@@ -5568,9 +5576,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
hmapx_add(&nd_changes->trk_lbs.crupdated, lb_dps);
}
}
-
- /* Re-evaluate 'od->has_lb_vip' */
- init_lb_for_datapath(od);
}
HMAP_FOR_EACH (clb, hmap_node, &trk_lb_data->crupdated_lbs) {
@@ -5583,15 +5588,9 @@ 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);
- }
- 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 ls datapath to the northd tracked data. */
+ hmapx_add(&nd_changes->ls_with_changed_lbs, od);
}
}
@@ -5613,17 +5612,14 @@ northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
for (size_t i = 0; i < lbgrp_dps->n_lr; i++) {
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);
}
for (size_t i = 0; i < lbgrp_dps->n_ls; i++) {
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, od);
}
/* Add the lb to the northd tracked data. */
@@ -5636,6 +5632,10 @@ northd_handle_lb_data_changes(struct tracked_lb_data *trk_lb_data,
nd_changes->type |= NORTHD_TRACKED_LBS;
}
+ if (!hmapx_is_empty(&nd_changes->ls_with_changed_lbs)) {
+ nd_changes->type |= NORTHD_TRACKED_LS_LBS;
+ }
+
return true;
}
@@ -6569,63 +6569,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.
@@ -6773,9 +6716,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
@@ -6788,7 +6732,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;";
@@ -6807,7 +6751,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)
{
@@ -6828,7 +6772,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)
{
@@ -6858,9 +6802,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. */
@@ -6872,22 +6814,30 @@ 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_stateful_rec_pre_acls(
+ const struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ const struct ls_port_group_table *ls_port_groups,
+ struct hmap *lflows)
+{
/* 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_stateful_rec->has_stateful_acl) {
for (size_t i = 0; i < od->n_router_ports; i++) {
struct ovn_port *op = od->router_ports[i];
if (op->enable_router_port_acl) {
continue;
}
- skip_port_from_conntrack(od, op,
+ skip_port_from_conntrack(od, op, 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);
@@ -6925,7 +6875,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_stateful_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);
@@ -7053,30 +7003,39 @@ 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_stateful_rec_pre_lb(const struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ struct hmap *lflows)
+{
for (size_t i = 0; i < od->n_router_ports; i++) {
skip_port_from_conntrack(od, od->router_ports[i],
+ ls_stateful_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_stateful_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_stateful_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.
*
@@ -7107,7 +7066,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_stateful_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,
@@ -7148,7 +7107,8 @@ build_pre_stateful(struct ovn_datapath *od,
}
static void
-build_acl_hints(struct ovn_datapath *od,
+build_acl_hints(const struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
const struct chassis_features *features,
struct hmap *lflows)
{
@@ -7175,13 +7135,14 @@ 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_stateful_rec->has_acls && !ls_stateful_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_stateful_rec->has_stateful_acl
+ && !ls_stateful_rec->has_lb_vip) {
continue;
}
@@ -7317,10 +7278,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"
@@ -7357,7 +7318,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;
}
@@ -7544,7 +7505,9 @@ 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_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ struct hmap *lflows,
const char *default_acl_action,
const struct shash *meter_groups,
struct ds *match,
@@ -7560,7 +7523,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_stateful_rec->max_acl_tier) {
ds_put_cstr(actions, REG_ACL_TIER " = 0; ");
}
@@ -7568,7 +7531,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_stateful_rec->has_acls) {
ovn_lflow_add(lflows, od, stage, 0, "1", "next;");
continue;
}
@@ -7603,7 +7566,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_stateful_rec->max_acl_tier; j++) {
ds_clear(match);
ds_put_format(match, REG_ACL_TIER " == %"PRIuSIZE, j);
ds_clear(&tier_actions);
@@ -7619,7 +7582,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,
@@ -7692,7 +7655,9 @@ 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_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ const struct chassis_features *features,
struct hmap *lflows,
const struct ls_port_group_table *ls_port_groups,
const struct shash *meter_groups)
@@ -7700,7 +7665,8 @@ build_acls(struct ovn_datapath *od, const struct chassis_features *features,
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_stateful_rec->has_stateful_acl
+ || ls_stateful_rec->has_lb_vip);
const char *ct_blocked_match = features->ct_no_masked_label
? "ct_mark.blocked"
: "ct_label.blocked";
@@ -7714,8 +7680,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_stateful_rec->has_acls) {
+ if (!ls_stateful_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",
@@ -7878,7 +7844,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_stateful_rec->max_acl_tier,
+ &match, &actions);
}
const struct ls_port_group *ls_pg =
@@ -7894,7 +7861,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_stateful_rec->max_acl_tier,
+ &match, &actions);
}
}
}
@@ -7912,7 +7880,7 @@ build_acls(struct ovn_datapath *od, const struct chassis_features *features,
dns_actions);
}
- if (od->has_acls || od->has_lb_vip) {
+ if (ls_stateful_rec->has_acls || ls_stateful_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,
@@ -7926,8 +7894,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_stateful_rec, od, lflows, default_acl_action,
+ meter_groups, &match, &actions);
ds_destroy(&match);
ds_destroy(&actions);
@@ -8574,7 +8542,9 @@ build_stateful(struct ovn_datapath *od,
}
static void
-build_lb_hairpin(struct ovn_datapath *od, struct hmap *lflows)
+build_lb_hairpin(const struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ struct hmap *lflows)
{
/* Ingress Pre-Hairpin/Nat-Hairpin/Hairpin tabled (Priority 0).
* Packets that don't need hairpinning should continue processing.
@@ -8583,7 +8553,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_stateful_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.
@@ -9241,9 +9211,7 @@ build_dhcpv4_options_flows(struct ovn_port *op,
&op->nbsp->dhcpv4_options->options, "lease_time");
ovs_assert(server_id && server_mac && lease_time);
const char *dhcp_actions =
- (op->od->has_stateful_acl || op->od->has_lb_vip)
- ? REGBIT_ACL_VERDICT_ALLOW" = 1; ct_commit; next;"
- : REGBIT_ACL_VERDICT_ALLOW" = 1; next;";
+ REGBIT_ACL_VERDICT_ALLOW" = 1; next;";
ds_clear(&match);
ds_put_format(&match, "outport == %s && eth.src == %s "
"&& ip4.src == %s && udp && udp.src == 67 "
@@ -9326,9 +9294,7 @@ build_dhcpv6_options_flows(struct ovn_port *op,
ipv6_string_mapped(server_ip, &lla);
const char *dhcp6_actions =
- (op->od->has_stateful_acl || op->od->has_lb_vip)
- ? REGBIT_ACL_VERDICT_ALLOW" = 1; ct_commit; next;"
- : REGBIT_ACL_VERDICT_ALLOW" = 1; next;";
+ REGBIT_ACL_VERDICT_ALLOW" = 1; next;";
ds_clear(&match);
ds_put_format(&match, "outport == %s && eth.src == %s "
"&& ip6.src == %s && udp && udp.src == 547 "
@@ -9435,22 +9401,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);
}
@@ -15590,7 +15550,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_stateful_rec->has_lb_vip && features->ct_lb_related) {
ds_clear(match);
ds_put_cstr(match, "ct.rel && !ct.est && !ct.new");
@@ -15615,7 +15575,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_stateful_rec->has_lb_vip) {
ds_clear(match);
ds_put_format(match, "ct.est && !ct.rel && !ct.new && %s.natted",
@@ -15645,7 +15605,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_stateful_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;");
@@ -16070,6 +16030,21 @@ build_lr_stateful_flows(const struct lr_stateful_record *lr_stateful_rec,
meter_groups);
}
+static void
+build_ls_stateful_flows(const struct ls_stateful_record *ls_stateful_rec,
+ const struct ovn_datapath *od,
+ const struct ls_port_group_table *ls_pgs,
+ const struct chassis_features *features,
+ const struct shash *meter_groups,
+ struct hmap *lflows)
+{
+ build_ls_stateful_rec_pre_acls(ls_stateful_rec, od, ls_pgs, lflows);
+ build_ls_stateful_rec_pre_lb(ls_stateful_rec, od, lflows);
+ build_acl_hints(ls_stateful_rec, od, features, lflows);
+ build_acls(ls_stateful_rec, od, features, lflows, ls_pgs, meter_groups);
+ build_lb_hairpin(ls_stateful_rec, od, lflows);
+}
+
struct lswitch_flow_build_info {
const struct ovn_datapaths *ls_datapaths;
const struct ovn_datapaths *lr_datapaths;
@@ -16077,6 +16052,7 @@ struct lswitch_flow_build_info {
const struct hmap *lr_ports;
const struct ls_port_group_table *ls_port_groups;
const struct lr_stateful_table *lr_stateful_table;
+ const struct ls_stateful_table *ls_stateful_table;
struct hmap *lflows;
struct hmap *igmp_groups;
const struct shash *meter_groups;
@@ -16101,9 +16077,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);
@@ -16221,6 +16195,7 @@ build_lflows_thread(void *arg)
{
struct worker_control *control = (struct worker_control *) arg;
const struct lr_stateful_record *lr_stateful_rec;
+ const struct ls_stateful_record *ls_stateful_rec;
struct lswitch_flow_build_info *lsi;
struct ovn_igmp_group *igmp_group;
struct ovn_lb_datapaths *lb_dps;
@@ -16345,6 +16320,26 @@ build_lflows_thread(void *arg)
lsi->features);
}
}
+
+ for (bnum = control->id;
+ bnum <= lsi->ls_stateful_table->entries.mask;
+ bnum += control->pool->size)
+ {
+ LS_STATEFUL_TABLE_FOR_EACH_IN_P (ls_stateful_rec, bnum,
+ lsi->ls_stateful_table) {
+ od = ovn_datapaths_find_by_index(
+ lsi->ls_datapaths, ls_stateful_rec->ls_index);
+ /* Make sure that ls_stateful_rec and od belong to the
+ * same NB Logical switch. */
+ ovs_assert(uuid_equals(&ls_stateful_rec->nbs_uuid,
+ &od->nbs->header_.uuid));
+ build_ls_stateful_flows(ls_stateful_rec, od,
+ lsi->ls_port_groups,
+ lsi->features, lsi->meter_groups,
+ lsi->lflows);
+ }
+ }
+
for (bnum = control->id;
bnum <= lsi->igmp_groups->mask;
bnum += control->pool->size)
@@ -16406,6 +16401,7 @@ build_lswitch_and_lrouter_flows(
const struct hmap *lr_ports,
const struct ls_port_group_table *ls_pgs,
const struct lr_stateful_table *lr_stateful_table,
+ const struct ls_stateful_table *ls_stateful_table,
struct hmap *lflows,
struct hmap *igmp_groups,
const struct shash *meter_groups,
@@ -16436,6 +16432,7 @@ build_lswitch_and_lrouter_flows(
lsiv[index].lr_ports = lr_ports;
lsiv[index].ls_port_groups = ls_pgs;
lsiv[index].lr_stateful_table = lr_stateful_table;
+ lsiv[index].ls_stateful_table = ls_stateful_table;
lsiv[index].igmp_groups = igmp_groups;
lsiv[index].meter_groups = meter_groups;
lsiv[index].lb_dps_map = lb_dps_map;
@@ -16461,6 +16458,7 @@ build_lswitch_and_lrouter_flows(
free(lsiv);
} else {
const struct lr_stateful_record *lr_stateful_rec;
+ const struct ls_stateful_record *ls_stateful_rec;
struct ovn_igmp_group *igmp_group;
struct ovn_lb_datapaths *lb_dps;
struct ovn_datapath *od;
@@ -16473,6 +16471,7 @@ build_lswitch_and_lrouter_flows(
.lr_ports = lr_ports,
.ls_port_groups = ls_pgs,
.lr_stateful_table = lr_stateful_table,
+ .ls_stateful_table = ls_stateful_table,
.lflows = lflows,
.igmp_groups = igmp_groups,
.meter_groups = meter_groups,
@@ -16542,6 +16541,19 @@ build_lswitch_and_lrouter_flows(
lsi.meter_groups, lsi.features);
}
stopwatch_stop(LFLOWS_LR_STATEFUL_STOPWATCH_NAME, time_msec());
+ stopwatch_start(LFLOWS_LS_STATEFUL_STOPWATCH_NAME, time_msec());
+ LS_STATEFUL_TABLE_FOR_EACH (ls_stateful_rec, ls_stateful_table) {
+ od = ovn_datapaths_find_by_index(lsi.ls_datapaths,
+ ls_stateful_rec->ls_index);
+ /* Make sure that ls_stateful_rec and od belong to the
+ * same NB Logical switch. */
+ ovs_assert(uuid_equals(&ls_stateful_rec->nbs_uuid,
+ &od->nbs->header_.uuid));
+ build_ls_stateful_flows(ls_stateful_rec, od, lsi.ls_port_groups,
+ lsi.features, lsi.meter_groups,
+ lsi.lflows);
+ }
+ stopwatch_stop(LFLOWS_LS_STATEFUL_STOPWATCH_NAME, time_msec());
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,
@@ -16638,6 +16650,7 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
input_data->lr_ports,
input_data->ls_port_groups,
input_data->lr_stateful_table,
+ input_data->ls_stateful_table,
lflows,
&igmp_groups,
input_data->meter_groups,
@@ -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 *' */
@@ -118,6 +120,8 @@ enum northd_tracked_data_type {
NORTHD_TRACKED_PORTS = (1 << 0),
NORTHD_TRACKED_LBS = (1 << 1),
NORTHD_TRACKED_LR_NATS = (1 << 2),
+ NORTHD_TRACKED_LS_LBS = (1 << 3),
+ NORTHD_TRACKED_LS_ACLS = (1 << 4),
};
/* Track what's changed in the northd engine node.
@@ -132,6 +136,14 @@ struct northd_tracked_data {
/* Tracked logical routers whose NATs have changed.
* hmapx node is 'struct ovn_datapath *'. */
struct hmapx trk_nat_lrs;
+
+ /* Tracked logical switches whose load balancers have changed.
+ * hmapx node is 'struct ovn_datapath *'. */
+ struct hmapx ls_with_changed_lbs;
+
+ /* Tracked logical switches whose ACLs have changed.
+ * hmapx node is 'struct ovn_datapath *'. */
+ struct hmapx ls_with_changed_acls;
};
struct northd_data {
@@ -180,6 +192,7 @@ struct lflow_input {
const struct hmap *lr_ports;
const struct ls_port_group_table *ls_port_groups;
const struct lr_stateful_table *lr_stateful_table;
+ const struct ls_stateful_table *ls_stateful_table;
const struct shash *meter_groups;
const struct hmap *lb_datapaths_map;
const struct hmap *bfd_connections;
@@ -289,11 +302,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;
@@ -548,6 +557,18 @@ northd_has_lr_nats_in_tracked_data(struct northd_tracked_data *trk_nd_changes)
return trk_nd_changes->type & NORTHD_TRACKED_LR_NATS;
}
+static inline bool
+northd_has_ls_lbs_in_tracked_data(struct northd_tracked_data *trk_nd_changes)
+{
+ return trk_nd_changes->type & NORTHD_TRACKED_LS_LBS;
+}
+
+static inline bool
+northd_has_ls_acls_in_tracked_data(struct northd_tracked_data *trk_nd_changes)
+{
+ return trk_nd_changes->type & NORTHD_TRACKED_LS_ACLS;
+}
+
/* Returns 'true' if the IPv4 'addr' is on the same subnet with one of the
* IPs configured on the router port.
*/
@@ -559,4 +580,10 @@ bool lrouter_port_ipv4_reachable(const struct ovn_port *, ovs_be32 addr);
bool lrouter_port_ipv6_reachable(const struct ovn_port *,
const struct in6_addr *);
+static inline bool
+lr_has_multiple_gw_ports(const struct ovn_datapath *od)
+{
+ return od->n_l3dgw_ports > 1 && !od->is_gw_router;
+}
+
#endif /* NORTHD_H */
@@ -874,6 +874,7 @@ main(int argc, char *argv[])
stopwatch_create(LFLOWS_PORTS_STOPWATCH_NAME, SW_MS);
stopwatch_create(LFLOWS_LBS_STOPWATCH_NAME, SW_MS);
stopwatch_create(LFLOWS_LR_STATEFUL_STOPWATCH_NAME, SW_MS);
+ stopwatch_create(LFLOWS_LS_STATEFUL_STOPWATCH_NAME, SW_MS);
stopwatch_create(LFLOWS_IGMP_STOPWATCH_NAME, SW_MS);
stopwatch_create(LFLOWS_DP_GROUPS_STOPWATCH_NAME, SW_MS);
stopwatch_create(LFLOWS_TO_SB_STOPWATCH_NAME, SW_MS);
@@ -881,6 +882,7 @@ main(int argc, char *argv[])
stopwatch_create(SYNC_METERS_RUN_STOPWATCH_NAME, SW_MS);
stopwatch_create(LR_NAT_RUN_STOPWATCH_NAME, SW_MS);
stopwatch_create(LR_STATEFUL_RUN_STOPWATCH_NAME, SW_MS);
+ stopwatch_create(LS_STATEFUL_RUN_STOPWATCH_NAME, SW_MS);
/* Initialize incremental processing engine for ovn-northd */
inc_proc_northd_init(&ovnnb_idl_loop, &ovnsb_idl_loop);