[ovs-dev] [PATCH ovn v3 07/10] ovn: Add tunnel_key concept to Bindings table, assign in ovn-northd.

Ben Pfaff blp at nicira.com
Wed Apr 29 01:06:55 UTC 2015


On Tue, Apr 28, 2015 at 05:20:13PM -0700, Justin Pettit wrote:
> 
> > On Apr 24, 2015, at 3:34 PM, Ben Pfaff <blp at nicira.com> wrote:
> > 
> > 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 <blp at nicira.com>
> 
> This patch (and the implementation that follows) goes the route of
> having each logical port have a unique tunnel key.  I had envisioned a
> different approach.  I'll briefly write it down here, but then it's
> probably worth discussing it more fully in a different forum (or at
> least a different thread).  I was thinking that we'd use a 24-bit VNI
> that would identify a logical switch.  In Geneve, it would be in the
> VNI field, and in STT, it would be in the lower 24 bits of the 64 bit
> key.  The local OVS would receive packets from a particular VNI, and
> then look at the DMAC to figure out which logical ports should receive
> it.
> 
> For port security, we would add a logical port that would uniquely
> identify a port within a particular VNI (ie, the value is only unique
> for a particular VNI).  For this, the logical port would only be used
> to identify the ingress port, since the receiving OVS would still be
> responsible for local replication and enforcing policies.  We would
> store the logical port in a TLV extension in Geneve and in the next
> (let's say) 16 bits of the STT header.
> 
> Okay, now that I've written that down, I'll review the code as you've
> implemented it.  We can have a follow-up discussion on the relative
> merits of each approach.

I have some thoughts on the above, but I also would like to discuss it
separately.  Do you want to repost the above as a new thread?  Or do you
want to discuss some other way?

> > +            /* Choose unique tunnel_key for the logical port. */
> > +            static uint16_t next_tunnel_key = 1;
> > +            while (tunnel_key_in_use(&tk_hmap, next_tunnel_key)) {
> > +                next_tunnel_key++;
> > +            }
> > +            sbrec_bindings_set_tunnel_key(binding, next_tunnel_key++);
> 
> Were you trying to avoid a tunnel key of zero?  If so, I think we'll
> hit that on wrap-around.  Also, if we have 64K logical ports, I think
> this will get stuck in a permanent loop with no way to get out, since
> it's the only thing that ever deletes a tunnel_key.  Finally, should
> you add the new tunnel to tk_hmap so that if we're anywhere near full
> that we don't assign the same tunnel key twice?

Yes, I was trying to avoid a tunnel key of 0 (the schema prohibits 0; I
use it in the pipeline as a special value), and I didn't expect that
we'd get anywhere near 64k ports (initially) so I didn't worry about
that case.  Anyway, it's better to avoid the problem, and not too hard.
I've appended an incremental diff to this email.

> > +    struct binding_hash_node *hash_node;
> > +    HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) {
> > +        hmap_remove(&lp_hmap, &hash_node->lp_node);
> 
> Should you be using HMAP_FOR_EACH_SAFE()?

That's necessary if the loop actually modifies or frees the hmap_node or
the structure that contains it (which is the usual case), but this loop
doesn't do that.

> > +                "tunnel_key": {
> > +                     "type": {"key": {"type": "integer",
> > +                                      "minInteger": 1,
> > +                                      "maxInteger": 65535}}},
> 
> If we decide to go with a unique id for each logical port, I do worry
> that 64K is too small for the total number of logical ports supported
> by OVN.

Yes, I do think that we need to scope it within a logical switch.

--8<--------------------------cut here-------------------------->8--

diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 09a98cc..0b5becf 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -464,6 +464,25 @@ tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key)
     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
@@ -545,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,
@@ -554,14 +578,16 @@ set_bindings(struct northd_context *ctx)
                 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
             }
 
-            /* Choose unique tunnel_key for the logical port. */
-            static uint16_t next_tunnel_key = 1;
-            while (tunnel_key_in_use(&tk_hmap, next_tunnel_key)) {
-                next_tunnel_key++;
-            }
-            sbrec_bindings_set_tunnel_key(binding, next_tunnel_key++);
-
+            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));
         }
     }
 
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 5eabf32..4fb7529 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -666,7 +666,16 @@
     </column>
 
     <column name="tunnel_key">
-      A number that represents the logical port in tunnel IDs.
+      <p>
+        A number that represents the logical port in the IDs carried within
+        tunnel protocol packets.  (This avoids wasting space for a whole UUID
+        in tunneled packets.  It 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">



More information about the dev mailing list