When packets travel among nodes in OVN over tunnels, a tunnel key value is
needed to convey the logical port to which the packet is destined.  This
commit adds a tunnel_key column to the Bindings table and adds code to
ovn-northd to assign a unique tunnel_key value to each logical port.

Signed-off-by: Ben Pfaff <b...@nicira.com>
---
 ovn/northd/ovn-northd.c | 104 ++++++++++++++++++++++++++++++++++++++----------
 ovn/ovn-sb.ovsschema    |   6 ++-
 ovn/ovn-sb.xml          |  14 +++++++
 3 files changed, 102 insertions(+), 22 deletions(-)

diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 34ac995..0b5becf 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -444,6 +444,45 @@ tags_equal(const struct sbrec_bindings *binding,
     return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
 }
 
+struct binding_hash_node {
+    struct hmap_node lp_node; /* In 'lp_map', by binding->logical_port. */
+    struct hmap_node tk_node; /* In 'tk_map', by binding->tunnel_key. */
+    const struct sbrec_bindings *binding;
+};
+
+static bool
+tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key)
+{
+    const struct binding_hash_node *hash_node;
+
+    HMAP_FOR_EACH_IN_BUCKET (hash_node, tk_node, hash_int(tunnel_key, 0),
+                             tk_hmap) {
+        if (hash_node->binding->tunnel_key == tunnel_key) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Chooses and returns a positive tunnel key that is not already in use in
+ * 'tk_hmap'.  Returns 0 if all tunnel keys are in use. */
+static uint16_t
+choose_tunnel_key(const struct hmap *tk_hmap)
+{
+    static uint16_t prev;
+
+    for (uint16_t key = prev + 1; key != prev; key++) {
+        if (!tunnel_key_in_use(tk_hmap, key)) {
+            prev = key;
+            return key;
+        }
+    }
+
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+    VLOG_WARN_RL(&rl, "all tunnel keys exhausted");
+    return 0;
+}
+
 /*
  * When a change has occurred in the OVN_Northbound database, we go through and
  * make sure that the contents of the Bindings table in the OVN_Southbound
@@ -453,38 +492,41 @@ tags_equal(const struct sbrec_bindings *binding,
 static void
 set_bindings(struct northd_context *ctx)
 {
-    struct hmap bindings_hmap;
     const struct sbrec_bindings *binding;
     const struct nbrec_logical_port *lport;
 
-    struct binding_hash_node {
-        struct hmap_node node;
-        const struct sbrec_bindings *binding;
-    } *hash_node, *hash_node_next;
-
     /*
      * We will need to look up a binding for every logical port.  We don't want
      * to have to do an O(n) search for every binding, so start out by hashing
      * them on the logical port.
      *
      * As we go through every logical port, we will update the binding if it
-     * exists or create one otherwise.  When the update is done, we'll remove 
it
-     * from the hashmap.  At the end, any bindings left in the hashmap are for
-     * logical ports that have been deleted.
+     * exists or create one otherwise.  When the update is done, we'll remove
+     * it from the hashmap.  At the end, any bindings left in the hashmap are
+     * for logical ports that have been deleted.
+     *
+     * We index the logical_port column because that's the shared key between
+     * the OVN_NB and OVN_SB databases.  We index the tunnel_key column to
+     * allow us to choose a unique tunnel key for any Binding rows we have to
+     * add.
      */
-    hmap_init(&bindings_hmap);
+    struct hmap lp_hmap = HMAP_INITIALIZER(&lp_hmap);
+    struct hmap tk_hmap = HMAP_INITIALIZER(&tk_hmap);
 
     SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
-        hash_node = xzalloc(sizeof *hash_node);
+        struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
         hash_node->binding = binding;
-        hmap_insert(&bindings_hmap, &hash_node->node,
-                hash_string(binding->logical_port, 0));
+        hmap_insert(&lp_hmap, &hash_node->lp_node,
+                    hash_string(binding->logical_port, 0));
+        hmap_insert(&tk_hmap, &hash_node->tk_node,
+                    hash_int(binding->tunnel_key, 0));
     }
 
     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
+        struct binding_hash_node *hash_node;
         binding = NULL;
-        HMAP_FOR_EACH_WITH_HASH(hash_node, node,
-                hash_string(lport->name, 0), &bindings_hmap) {
+        HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
+                                hash_string(lport->name, 0), &lp_hmap) {
             if (!strcmp(lport->name, hash_node->binding->logical_port)) {
                 binding = hash_node->binding;
                 break;
@@ -502,9 +544,7 @@ set_bindings(struct northd_context *ctx)
             /* We found an existing binding for this logical port.  Update its
              * contents. */
 
-            hmap_remove(&bindings_hmap, &hash_node->node);
-            free(hash_node);
-            hash_node = NULL;
+            hmap_remove(&lp_hmap, &hash_node->lp_node);
 
             if (!macs_equal(binding->mac, binding->n_mac,
                         lport->macs, lport->n_macs)) {
@@ -524,6 +564,11 @@ set_bindings(struct northd_context *ctx)
         } else {
             /* There is no binding for this logical port, so create one. */
 
+            uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
+            if (!tunnel_key) {
+                continue;
+            }
+
             binding = sbrec_bindings_insert(ctx->ovnsb_txn);
             sbrec_bindings_set_logical_port(binding, lport->name);
             sbrec_bindings_set_mac(binding,
@@ -533,16 +578,32 @@ set_bindings(struct northd_context *ctx)
                 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
             }
 
+            sbrec_bindings_set_tunnel_key(binding, tunnel_key);
             sbrec_bindings_set_logical_datapath(binding, logical_datapath);
+
+            /* Add the tunnel key to the tk_hmap so that we don't try to use it
+             * for another port.  (We don't want it in the lp_hmap because that
+             * would just get the Bindings record deleted later.) */
+            struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
+            hash_node->binding = binding;
+            hmap_insert(&tk_hmap, &hash_node->tk_node,
+                        hash_int(binding->tunnel_key, 0));
         }
     }
 
-    HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
-        hmap_remove(&bindings_hmap, &hash_node->node);
+    struct binding_hash_node *hash_node;
+    HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) {
+        hmap_remove(&lp_hmap, &hash_node->lp_node);
         sbrec_bindings_delete(hash_node->binding);
+    }
+    hmap_destroy(&lp_hmap);
+
+    struct binding_hash_node *hash_node_next;
+    HMAP_FOR_EACH_SAFE (hash_node, hash_node_next, tk_node, &tk_hmap) {
+        hmap_remove(&tk_hmap, &hash_node->tk_node);
         free(hash_node);
     }
-    hmap_destroy(&bindings_hmap);
+    hmap_destroy(&tk_hmap);
 }
 
 static void
@@ -733,6 +794,7 @@ main(int argc, char *argv[])
     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag);
     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port);
     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_datapath);
+    ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tunnel_key);
     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
     ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema
index 5f2d1a4..9fd5363 100644
--- a/ovn/ovn-sb.ovsschema
+++ b/ovn/ovn-sb.ovsschema
@@ -47,6 +47,10 @@
             "columns": {
                 "logical_datapath": {"type": "uuid"},
                 "logical_port": {"type": "string"},
+                "tunnel_key": {
+                     "type": {"key": {"type": "integer",
+                                      "minInteger": 1,
+                                      "maxInteger": 65535}}},
                 "parent_port": {"type": {"key": "string", "min": 0, "max": 1}},
                 "tag": {
                      "type": {"key": {"type": "integer",
@@ -57,6 +61,6 @@
                 "mac": {"type": {"key": "string",
                                  "min": 0,
                                  "max": "unlimited"}}},
-            "indexes": [["logical_port"]],
+            "indexes": [["logical_port"], ["tunnel_key"]],
             "isRoot": true}},
     "version": "1.0.0"}
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 1a27617..31ee871 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -665,6 +665,20 @@
       prescribe a particular format for the logical port ID.
     </column>
 
+    <column name="tunnel_key">
+      <p>
+        A number that represents the logical port in the key (e.g. VXLAN VNI or
+        STT key) field carried within tunnel protocol packets.  (This avoids
+        wasting space for a whole UUID in tunneled packets.  It also allows OVN
+        to support encapsulations that cannot fit an entire UUID in their
+        tunnel keys.)
+      </p>
+
+      <p>
+        Tunnel ID 0 is reserved for internal use within OVN.
+      </p>
+    </column>
+
     <column name="parent_port">
       For containers created inside a VM, this is taken from
       <ref table="Logical_Port" column="parent_name" db="OVN_Northbound"/>
-- 
2.1.3

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to