When generating SB records that have tunnel_keys (e.g., Datapath,
Port_Binding, Multicast_Group) ovn-northd tries to reuse the
tunnel_key value from the original SB record, if any available.

However, there's no check for tunnel_keys that would conflict with
newly allocated keys for new records. In order to avoid that,
we first check that the tunnel_key value in the SB record doesn't
match a key already allocated by northd in the current run.
If there's a conflict, ovn-north will generate a new key for the SB
record.

One way to reproduce the issue is:
$ ovn-nbctl ls-add ls1
$ ovn-nbctl ls-add ls2
$ ovn-nbctl lsp-add ls1 lsp1
$ ovn-nbctl lsp-add ls2 lsp2
$ ovn-nbctl --wait=sb sync
$ ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2

Reported-by: Dan Williams <d...@redhat.com>
Reported-at: https://bugzilla.redhat.com/1828637
Signed-off-by: Dumitru Ceara <dce...@redhat.com>
---
 northd/ovn-northd.c |   27 +++++++++++++++++++++++----
 tests/ovn-northd.at |   30 ++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index de59452..dad6a45 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -1068,7 +1068,13 @@ build_datapaths(struct northd_context *ctx, struct hmap 
*datapaths,
     struct ovn_datapath *od, *next;
     if (!ovs_list_is_empty(&nb_only) || !ovs_list_is_empty(&both)) {
         LIST_FOR_EACH (od, list, &both) {
-            ovn_add_tnlid(&dp_tnlids, od->sb->tunnel_key);
+            if (!ovn_tnlid_in_use(&dp_tnlids, od->sb->tunnel_key)) {
+                ovn_add_tnlid(&dp_tnlids, od->sb->tunnel_key);
+            } else {
+                uint32_t dp_key = ovn_datapath_allocate_key(&dp_tnlids);
+
+                sbrec_datapath_binding_set_tunnel_key(od->sb, dp_key);
+            }
         }
     }
 
@@ -3465,7 +3471,12 @@ build_ports(struct northd_context *ctx,
     /* For logical ports that are in both databases, index the in-use
      * tunnel_keys. */
     LIST_FOR_EACH (op, list, &both) {
-        ovn_add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);
+        if (!ovn_tnlid_in_use(&op->od->port_tnlids, op->sb->tunnel_key)) {
+            ovn_add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);
+        } else {
+            sbrec_port_binding_set_tunnel_key(op->sb,
+                                              ovn_port_allocate_key(op->od));
+        }
         if (op->sb->tunnel_key > op->od->port_key_hint) {
             op->od->port_key_hint = op->sb->tunnel_key;
         }
@@ -3727,8 +3738,16 @@ ovn_igmp_group_add(struct northd_context *ctx, struct 
hmap *igmp_groups,
         igmp_group->address = *address;
         if (mcgroup) {
             igmp_group->mcgroup.key = mcgroup->tunnel_key;
-            ovn_add_tnlid(&datapath->mcast_info.group_tnlids,
-                          mcgroup->tunnel_key);
+            if (!ovn_tnlid_in_use(&datapath->mcast_info.group_tnlids,
+                                  mcgroup->tunnel_key)) {
+                ovn_add_tnlid(&datapath->mcast_info.group_tnlids,
+                              mcgroup->tunnel_key);
+            } else {
+                uint32_t group_key =
+                    ovn_mcast_group_allocate_key(&datapath->mcast_info);
+
+                sbrec_multicast_group_set_tunnel_key(mcgroup, group_key);
+            }
         } else {
             igmp_group->mcgroup.key = 0;
         }
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index a4469c7..4823d14 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -1414,3 +1414,33 @@ lr_uuid=$(ovn-nbctl --columns _uuid list Logical_Router 
.)
 AT_CHECK[test ${nb_uuid} = ${lr_uuid}]
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- check reconcile stale tunnel keys])
+ovn_start
+
+ovn-nbctl ls-add ls1
+ovn-nbctl ls-add ls2
+ovn-nbctl lsp-add ls1 lsp1
+ovn-nbctl lsp-add ls2 lsp2
+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
+
+# Ports are bound on different datapaths so it's expected that they both
+# get tunnel_key == 1.
+AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
+port_binding logical_port=lsp1)])
+AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
+port_binding logical_port=lsp2)])
+
+ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
+AT_CHECK([ovn-nbctl --wait=sb sync], [0])
+
+pb1_key=$(ovn-sbctl --bare --columns tunnel_key find port_binding \
+logical_port=lsp1)
+pb2_key=$(ovn-sbctl --bare --columns tunnel_key find port_binding \
+logical_port=lsp2)
+
+# ovn-northd should allocate a new tunnel_key for lsp1 or lsp2 to maintain
+# unique DB indices.
+AT_CHECK([test ${pb1_key} != ${pb2_key}])
+
+AT_CLEANUP

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

Reply via email to