This introduces a new southbound table called ACL_ID for storing
persistent ACL conntrack IDs. These IDs are generated by ovn-northd for
any ACL that is of type "allow-established".

Signed-off-by: Mark Michelson <mmich...@redhat.com>
---
 northd/automake.mk       |   2 +
 northd/en-acl-ids.c      | 206 +++++++++++++++++++++++++++++++++++++++
 northd/en-acl-ids.h      |  17 ++++
 northd/en-northd.c       |   6 ++
 northd/inc-proc-northd.c |  15 ++-
 northd/northd.c          |   3 +
 northd/northd.h          |   4 +
 northd/ovn-northd.c      |   4 +
 ovn-sb.ovsschema         |  14 ++-
 ovn-sb.xml               |  13 +++
 tests/ovn.at             |  39 ++++++++
 11 files changed, 319 insertions(+), 4 deletions(-)
 create mode 100644 northd/en-acl-ids.c
 create mode 100644 northd/en-acl-ids.h

diff --git a/northd/automake.mk b/northd/automake.mk
index 6566ad299..d46dfd763 100644
--- a/northd/automake.mk
+++ b/northd/automake.mk
@@ -34,6 +34,8 @@ northd_ovn_northd_SOURCES = \
        northd/en-ls-stateful.h \
        northd/en-sampling-app.c \
        northd/en-sampling-app.h \
+       northd/en-acl-ids.c \
+       northd/en-acl-ids.h \
        northd/inc-proc-northd.c \
        northd/inc-proc-northd.h \
        northd/ipam.c \
diff --git a/northd/en-acl-ids.c b/northd/en-acl-ids.c
new file mode 100644
index 000000000..897cc2da1
--- /dev/null
+++ b/northd/en-acl-ids.c
@@ -0,0 +1,206 @@
+/*
+ * 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 "en-acl-ids.h"
+#include "lib/uuidset.h"
+#include "lib/ovn-sb-idl.h"
+#include "lib/ovn-nb-idl.h"
+#include "lib/bitmap.h"
+
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(northd_acl_ids);
+
+enum id_state {
+    /* The ID represents a new northbound ACL that has
+     * not yet been synced to the southbound DB
+     */
+    ID_NEW,
+    /* The ID represents an ACL ID that has been synced
+     * with the southbound DB already
+     */
+    ID_SYNCED,
+    /* The ID represents a deleted NB ACL that also needs
+     * to be removed from the southbound DB
+     */
+    ID_INACTIVE,
+};
+
+struct acl_id {
+    struct hmap_node node;
+    int64_t id;
+    struct uuid nb_acl_uuid;
+    enum id_state state;
+};
+
+#define MAX_ACL_ID 65535
+
+struct acl_id_data {
+    struct hmap ids;
+    unsigned long *id_bitmap;
+};
+
+static void
+acl_id_data_init(struct acl_id_data *id_data)
+{
+    hmap_init(&id_data->ids);
+    id_data->id_bitmap = bitmap_allocate(MAX_ACL_ID);
+}
+
+static struct acl_id_data *
+acl_id_data_alloc(void)
+{
+    struct acl_id_data *id_data = xzalloc(sizeof *id_data);
+    acl_id_data_init(id_data);
+
+    return id_data;
+}
+
+static void
+acl_id_data_destroy(struct acl_id_data *id_data)
+{
+    struct acl_id *acl_id;
+    HMAP_FOR_EACH_POP (acl_id, node, &id_data->ids) {
+        free(acl_id);
+    }
+    hmap_destroy(&id_data->ids);
+    bitmap_free(id_data->id_bitmap);
+}
+
+void *
+en_acl_id_init(struct engine_node *node OVS_UNUSED,
+               struct engine_arg *arg OVS_UNUSED)
+{
+    struct acl_id_data *id_data = acl_id_data_alloc();
+    return id_data;
+}
+
+static void
+add_acl_id(struct hmap *id_map, int64_t id, enum id_state state,
+           const struct uuid *acl_uuid)
+{
+    struct acl_id *acl_id = xzalloc(sizeof *acl_id);
+    acl_id->id = id;
+    acl_id->state = state;
+    acl_id->nb_acl_uuid = *acl_uuid;
+    hmap_insert(id_map, &acl_id->node, uuid_hash(acl_uuid));
+}
+
+void
+en_acl_id_run(struct engine_node *node, void *data)
+{
+    const struct engine_context *eng_ctx = engine_get_context();
+    if (!eng_ctx->ovnnb_idl_txn || !eng_ctx->ovnsb_idl_txn) {
+        return;
+    }
+
+    const struct nbrec_acl_table *nb_acl_table =
+        EN_OVSDB_GET(engine_get_input("NB_acl", node));
+    const struct sbrec_acl_id_table *sb_acl_id_table =
+        EN_OVSDB_GET(engine_get_input("SB_acl_id", node));
+    struct uuidset visited = UUIDSET_INITIALIZER(&visited);
+    struct acl_id_data *id_data = data;
+
+    acl_id_data_destroy(id_data);
+    acl_id_data_init(id_data);
+
+    const struct nbrec_acl *nb_acl;
+    const struct sbrec_acl_id *sb_id;
+    SBREC_ACL_ID_TABLE_FOR_EACH (sb_id, sb_acl_id_table) {
+        nb_acl = nbrec_acl_table_get_for_uuid(nb_acl_table, &sb_id->nb_acl);
+        if (nb_acl && !strcmp(nb_acl->action, "allow-established")) {
+            bitmap_set1(id_data->id_bitmap, sb_id->id);
+            uuidset_insert(&visited, &sb_id->nb_acl);
+            add_acl_id(&id_data->ids, sb_id->id, ID_SYNCED, &sb_id->nb_acl);
+        } else {
+            /* NB ACL is deleted or has changed action type. This
+             * ID is no longer active.
+             */
+            add_acl_id(&id_data->ids, sb_id->id, ID_INACTIVE, &sb_id->nb_acl);
+        }
+    }
+
+    size_t scan_start = 1;
+    size_t scan_end = MAX_ACL_ID;
+    NBREC_ACL_TABLE_FOR_EACH (nb_acl, nb_acl_table) {
+        if (uuidset_find_and_delete(&visited, &nb_acl->header_.uuid)) {
+            continue;
+        }
+        if (strcmp(nb_acl->action, "allow-established")) {
+            continue;
+        }
+        int64_t new_id = bitmap_scan(id_data->id_bitmap, 0,
+                                     scan_start, scan_end + 1);
+        if (new_id == scan_end + 1) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "Exhausted all ACL IDs");
+            break;
+        }
+        add_acl_id(&id_data->ids, new_id, ID_NEW, &nb_acl->header_.uuid);
+        bitmap_set1(id_data->id_bitmap, new_id);
+        scan_start = new_id + 1;
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    uuidset_destroy(&visited);
+}
+
+void
+en_acl_id_cleanup(void *data)
+{
+    acl_id_data_destroy(data);
+}
+
+static const struct sbrec_acl_id *
+acl_id_lookup_by_id(struct ovsdb_idl_index *sbrec_acl_id_by_id,
+                    int64_t id)
+{
+    struct sbrec_acl_id *target = sbrec_acl_id_index_init_row(
+        sbrec_acl_id_by_id);
+    sbrec_acl_id_index_set_id(target, id);
+
+    struct sbrec_acl_id *retval = sbrec_acl_id_index_find(
+        sbrec_acl_id_by_id, target);
+
+    sbrec_acl_id_index_destroy_row(target);
+
+    return retval;
+}
+
+void sync_acl_ids(const struct acl_id_data *id_data,
+                  struct ovsdb_idl_txn *ovnsb_txn,
+                  struct ovsdb_idl_index *sbrec_acl_id_by_id)
+{
+    struct acl_id *acl_id;
+    const struct sbrec_acl_id *sb_id;
+    HMAP_FOR_EACH (acl_id, node, &id_data->ids) {
+        switch (acl_id->state) {
+        case ID_NEW:
+            sb_id = sbrec_acl_id_insert(ovnsb_txn);
+            sbrec_acl_id_set_id(sb_id, acl_id->id);
+            sbrec_acl_id_set_nb_acl(sb_id, acl_id->nb_acl_uuid);
+            break;
+        case ID_INACTIVE:
+            sb_id = acl_id_lookup_by_id(sbrec_acl_id_by_id, acl_id->id);
+            if (sb_id) {
+                sbrec_acl_id_delete(sb_id);
+            }
+            break;
+        case ID_SYNCED:
+            break;
+        }
+    }
+}
diff --git a/northd/en-acl-ids.h b/northd/en-acl-ids.h
new file mode 100644
index 000000000..8b60b3c7c
--- /dev/null
+++ b/northd/en-acl-ids.h
@@ -0,0 +1,17 @@
+#ifndef EN_ACL_IDS_H
+#define EN_ACL_IDS_H
+
+#include <config.h>
+#include <stdbool.h>
+
+#include "lib/inc-proc-eng.h"
+
+bool northd_acl_id_handler(struct engine_node *node, void *data);
+void *en_acl_id_init(struct engine_node *, struct engine_arg *);
+void en_acl_id_run(struct engine_node *, void *data);
+void en_acl_id_cleanup(void *data);
+
+struct acl_id_data;
+void sync_acl_ids(const struct acl_id_data *, struct ovsdb_idl_txn *,
+                  struct ovsdb_idl_index *sbrec_acl_id_by_id);
+#endif
diff --git a/northd/en-northd.c b/northd/en-northd.c
index c7d1ebcb3..5545fe7a6 100644
--- a/northd/en-northd.c
+++ b/northd/en-northd.c
@@ -61,6 +61,10 @@ northd_get_input_data(struct engine_node *node,
         engine_ovsdb_node_get_index(
             engine_get_input("SB_fdb", node),
             "sbrec_fdb_by_dp_and_port");
+    input_data->sbrec_acl_id_by_id =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_acl_id", node),
+            "sbrec_acl_id_by_id");
 
     input_data->nbrec_logical_switch_table =
         EN_OVSDB_GET(engine_get_input("NB_logical_switch", node));
@@ -110,6 +114,8 @@ northd_get_input_data(struct engine_node *node,
     input_data->svc_monitor_mac = global_config->svc_monitor_mac;
     input_data->svc_monitor_mac_ea = global_config->svc_monitor_mac_ea;
     input_data->features = &global_config->features;
+
+    input_data->acl_id_data = engine_get_input_data("acl_id", node);
 }
 
 void
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 6e0aa04c4..43abf042d 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -41,6 +41,7 @@
 #include "en-sampling-app.h"
 #include "en-sync-sb.h"
 #include "en-sync-from-sb.h"
+#include "en-acl-ids.h"
 #include "unixctl.h"
 #include "util.h"
 
@@ -102,7 +103,8 @@ static unixctl_cb_func chassis_features_list;
     SB_NODE(fdb, "fdb") \
     SB_NODE(static_mac_binding, "static_mac_binding") \
     SB_NODE(chassis_template_var, "chassis_template_var") \
-    SB_NODE(logical_dp_group, "logical_dp_group")
+    SB_NODE(logical_dp_group, "logical_dp_group") \
+    SB_NODE(acl_id, "acl_id")
 
 enum sb_engine_node {
 #define SB_NODE(NAME, NAME_STR) SB_##NAME,
@@ -161,6 +163,7 @@ static ENGINE_NODE(route_policies, "route_policies");
 static ENGINE_NODE(routes, "routes");
 static ENGINE_NODE(bfd, "bfd");
 static ENGINE_NODE(bfd_sync, "bfd_sync");
+static ENGINE_NODE(acl_id, "acl_id");
 
 void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                           struct ovsdb_idl_loop *sb)
@@ -186,6 +189,9 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
                      global_config_sb_chassis_handler);
     engine_add_input(&en_global_config, &en_sampling_app, NULL);
 
+    engine_add_input(&en_acl_id, &en_nb_acl, NULL);
+    engine_add_input(&en_acl_id, &en_sb_acl_id, NULL);
+
     engine_add_input(&en_northd, &en_nb_mirror, NULL);
     engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL);
     engine_add_input(&en_northd, &en_nb_chassis_template_var, NULL);
@@ -201,8 +207,10 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_northd, &en_sb_static_mac_binding, NULL);
     engine_add_input(&en_northd, &en_sb_chassis_template_var, NULL);
     engine_add_input(&en_northd, &en_sb_fdb, northd_sb_fdb_change_handler);
+    engine_add_input(&en_northd, &en_sb_acl_id, NULL);
     engine_add_input(&en_northd, &en_global_config,
                      northd_global_config_handler);
+    engine_add_input(&en_northd, &en_acl_id, NULL);
 
     /* northd engine node uses the sb mac binding table to
      * cleanup mac binding entries for deleted logical ports
@@ -364,6 +372,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
         = mac_binding_by_datapath_index_create(sb->idl);
     struct ovsdb_idl_index *fdb_by_dp_key =
         ovsdb_idl_index_create1(sb->idl, &sbrec_fdb_col_dp_key);
+    struct ovsdb_idl_index *sbrec_acl_id_by_id =
+        ovsdb_idl_index_create1(sb->idl, &sbrec_acl_id_col_id);
 
     engine_init(&en_northd_output, &engine_arg);
 
@@ -388,6 +398,9 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_ovsdb_node_add_index(&en_sb_fdb,
                                 "fdb_by_dp_key",
                                 fdb_by_dp_key);
+    engine_ovsdb_node_add_index(&en_sb_acl_id,
+                                "sbrec_acl_id_by_id",
+                                sbrec_acl_id_by_id);
 
     struct ovsdb_idl_index *sbrec_address_set_by_name
         = ovsdb_idl_index_create1(sb->idl, &sbrec_address_set_col_name);
diff --git a/northd/northd.c b/northd/northd.c
index 0aae627a2..8dab88f62 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -50,6 +50,7 @@
 #include "en-lr-stateful.h"
 #include "en-ls-stateful.h"
 #include "en-sampling-app.h"
+#include "en-acl-ids.h"
 #include "lib/ovn-parallel-hmap.h"
 #include "ovn/actions.h"
 #include "ovn/features.h"
@@ -19120,6 +19121,8 @@ ovnnb_db_run(struct northd_input *input_data,
                      &data->ls_datapaths.datapaths);
     sync_template_vars(ovnsb_txn, input_data->nbrec_chassis_template_var_table,
                        input_data->sbrec_chassis_template_var_table);
+    sync_acl_ids(input_data->acl_id_data, ovnsb_txn,
+                 input_data->sbrec_acl_id_by_id);
 
     cleanup_stale_fdb_entries(input_data->sbrec_fdb_table,
                               &data->ls_datapaths.datapaths);
diff --git a/northd/northd.h b/northd/northd.h
index d60c944df..bccb1c5d8 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -63,12 +63,16 @@ struct northd_input {
     struct eth_addr svc_monitor_mac_ea;
     const struct chassis_features *features;
 
+    /* ACL ID inputs. */
+    const struct acl_id_data *acl_id_data;
+
     /* Indexes */
     struct ovsdb_idl_index *sbrec_chassis_by_name;
     struct ovsdb_idl_index *sbrec_chassis_by_hostname;
     struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
     struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
     struct ovsdb_idl_index *sbrec_fdb_by_dp_and_port;
+    struct ovsdb_idl_index *sbrec_acl_id_by_id;
 };
 
 /* A collection of datapaths. E.g. all logical switch datapaths, or all
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index fb29c3c21..bbb321248 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -896,6 +896,10 @@ main(int argc, char *argv[])
         ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
                              &sbrec_logical_dp_group_columns[i]);
     }
+    for (size_t i = 0; i < SBREC_ACL_ID_N_COLUMNS; i++) {
+        ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
+                             &sbrec_acl_id_columns[i]);
+    }
 
     unixctl_command_register("sb-connection-status", "", 0, 0,
                              ovn_conn_show, ovnsb_idl_loop.idl);
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index 73abf2c8d..837aee620 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "20.37.0",
-    "cksum": "1950136776 31493",
+    "version": "20.38.0",
+    "cksum": "4077385391 31851",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -617,6 +617,14 @@
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
             "indexes": [["chassis"]],
-            "isRoot": true}
+            "isRoot": true},
+       "ACL_ID": {
+           "columns": {
+               "id": {"type": {"key": {"type": "integer",
+                                              "minInteger": 0,
+                                              "maxInteger": 32767}}},
+               "nb_acl" : {"type": {"key": {"type": "uuid"}}}},
+           "indexes": [["id"]],
+           "isRoot": true}
     }
 }
diff --git a/ovn-sb.xml b/ovn-sb.xml
index ea4adc1c3..a3d71e100 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -5217,4 +5217,17 @@ tcp.flags = RST;
       The set of variable values for a given chassis.
     </column>
   </table>
+
+  <table name="ACL_ID">
+    <p>
+      Each record represents an identifier that <code>ovn-northd</code> needs
+      to synchronize with instances of <code>ovn-controller</code>.
+    </p>
+    <column name="id">
+      The actual identifier being synchronized.
+    </column>
+    <column name="nb_acl">
+      The Northbound ACL UUID for which this ID corresponds.
+    </column>
+  </table>
 </database>
diff --git a/tests/ovn.at b/tests/ovn.at
index 2fdf1a88c..53e6712cc 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -39761,3 +39761,42 @@ OVN_CLEANUP([hv1])
 
 AT_CLEANUP
 ])
+
+AT_SETUP([ACL Conntrack ID propagation])
+ovn_start
+
+dnl In this test, we want to ensure that southbound ACL_ID
+dnl entries are created for northbound ACLs of type "allow-established".
+dnl
+dnl If an ACL is of a different type, or if an ACL is deleted,
+dnl then there should be no soutbhound ACL_ID.
+
+check ovn-nbctl ls-add sw
+check ovn-nbctl --wait=sb acl-add sw from-lport 1000 1 allow-related
+acl_uuid=$(fetch_column nb:ACL _uuid priority=1000)
+
+dnl The ACL is not allow-established, so SBDB should have no rows.
+wait_row_count ACL_ID 0
+
+dnl When we change to allow-established, the SBDB should pick it up.
+check ovn-nbctl --wait=sb set ACL $acl_uuid action=allow-established
+
+wait_row_count ACL_ID 1
+
+dnl Setting to a new action should remove the row from the SBDB.
+check ovn-nbctl --wait=sb set ACL $acl_uuid action=drop
+
+wait_row_count ACL_ID 0
+
+dnl Set back to allow-established and make sure it shows up in the SBDB.
+check ovn-nbctl --wait=sb set ACL $acl_uuid action=allow-established
+
+wait_row_count ACL_ID 1
+
+dnl Delete the ACL and ensure the SBDB entry is deleted.
+check ovn-nbctl --wait=sb acl-del sw from-lport 1000 1
+
+wait_row_count ACL_ID 0
+
+AT_CLEANUP
+])
-- 
2.45.2

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to