We could end up with a stale neighbor entry when advertised DGP
migrates to a different chassis, that would be the case until the
next recompute. Make sure we check the chassis and properly reflect
the migration.

Fixes: 32d5252ceb65 ("controller, northd: Add an option to advertise FDB over 
EVPN.")
Assisted-by: GPT-6.1 Sol, OpenCode
Signed-off-by: Ales Musil <[email protected]>
---
 controller/ovn-controller.c | 27 ++++++++++++-
 tests/multinode.at          | 77 +++++++++++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 7bc3139ab..f4ec67e89 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -6742,6 +6742,18 @@ neighbor_sb_port_binding_handler(struct engine_node 
*node, void *data)
     struct ed_type_neighbor *ne_data = data;
     const struct sbrec_port_binding_table *pb_table =
         EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
+    const struct ovsrec_open_vswitch_table *ovs_table =
+    EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node));
+    struct ovsdb_idl_index *sbrec_chassis_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_chassis", node),
+            "name");
+
+    const char *chassis_id = get_ovs_chassis_id(ovs_table);
+    ovs_assert(chassis_id);
+    const struct sbrec_chassis *chassis =
+        chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
+    ovs_assert(chassis);
 
     const struct sbrec_port_binding *pb;
     SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb, pb_table) {
@@ -6751,8 +6763,19 @@ neighbor_sb_port_binding_handler(struct engine_node 
*node, void *data)
             return EN_HANDLED_UNCHANGED;
         }
 
-        if (sbrec_port_binding_is_updated(pb, SBREC_PORT_BINDING_COL_MAC) &&
-            sset_contains(&ne_data->advertised_pbs, pb->logical_port)) {
+        bool local = lport_pb_is_chassis_resident(chassis, pb);
+        enum sbrec_port_binding_column_id mac_col = SBREC_PORT_BINDING_COL_MAC;
+        if (sset_contains(&ne_data->advertised_pbs, pb->logical_port) &&
+            (sbrec_port_binding_is_updated(pb, mac_col) || !local)) {
+            return EN_UNHANDLED;
+        }
+
+        enum en_lport_type type = get_lport_type(pb);
+        enum sbrec_port_binding_column_id chassis_col =
+            SBREC_PORT_BINDING_COL_CHASSIS;
+        if (type == LP_CHASSISREDIRECT &&
+            sbrec_port_binding_is_updated(pb, chassis_col) &&
+            local) {
             return EN_UNHANDLED;
         }
     }
diff --git a/tests/multinode.at b/tests/multinode.at
index bd556b8f5..6292cc40b 100644
--- a/tests/multinode.at
+++ b/tests/multinode.at
@@ -4466,6 +4466,83 @@ OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ovs-ofctl dump-flows 
br-int table=OFTABLE_GET
 1
 ])
 
+AS_BOX([Check gateway EVPN advertisements follow HA ownership])
+# Both gateways already have local workloads on ls.  Migrating only the
+# chassisredirect binding must refresh advertisements even when neither
+# the local datapaths nor the Advertised_MAC_Binding rows change.
+gw1_chassis=$(m_fetch_column Chassis _uuid name=ovn-gw-1)
+gw2_chassis=$(m_fetch_column Chassis _uuid name=ovn-gw-2)
+check multinode_nbctl \
+    -- ha-chassis-group-add evpn-ha \
+    -- ha-chassis-group-add-chassis evpn-ha ovn-gw-1 20 \
+    -- ha-chassis-group-add-chassis evpn-ha ovn-gw-2 10
+evpn_ha=$(m_fetch_column nb:HA_Chassis_Group _uuid name=evpn-ha)
+check multinode_nbctl --wait=hv set logical_router_port lr-ls 
ha_chassis_group=$evpn_ha
+
+priority=20
+for gw in 1 2 1; do
+    active=ovn-gw-$gw
+    standby=ovn-gw-$((3 - gw))
+    if test "$gw" = 1; then
+        chassis=$gw1_chassis
+        host_ip=$host_bgp_ip_gw1
+    else
+        chassis=$gw2_chassis
+        host_ip=$host_bgp_ip_gw2
+    fi
+    check multinode_nbctl ha-chassis-group-add-chassis evpn-ha $active 
$priority
+    m_wait_row_count Port_Binding 1 logical_port=cr-lr-ls chassis=$chassis
+
+    # Ignore VLAN 1 copies, which are not managed by neighbor exchange.
+    # A desired static entry may retain extern_learn; do not require it
+    # to be cleared in order to recognize the local advertisement.
+    OVS_WAIT_UNTIL([m_as $active bridge fdb show dev lo-10 | \
+        grep -v ' vlan ' | grep '^00:00:00:01:00:00 ' | grep -q ' static'])
+    OVS_WAIT_FOR_OUTPUT([m_as $standby bridge fdb show dev lo-10 | \
+        grep -v ' vlan ' | grep -q '^00:00:00:01:00:00 '], [1])
+    OVS_WAIT_UNTIL([m_as $active ip -4 neigh show to 10.0.0.1 dev br-10 nud 
all | \
+        grep 'lladdr 00:00:00:01:00:00 ' | grep -q NOARP])
+    OVS_WAIT_UNTIL([m_as $active ip -6 neigh show to 10::1 dev br-10 nud all | 
\
+        grep 'lladdr 00:00:00:01:00:00 ' | grep -q NOARP])
+    OVS_WAIT_FOR_OUTPUT([m_as $standby ip -4 neigh show to 10.0.0.1 dev br-10 
nud all | \
+        grep -q 'lladdr 00:00:00:01:00:00 '], [1])
+    OVS_WAIT_FOR_OUTPUT([m_as $standby ip -6 neigh show to 10::1 dev br-10 nud 
all | \
+        grep -q 'lladdr 00:00:00:01:00:00 '], [1])
+
+    OVS_WAIT_FOR_OUTPUT_UNQUOTED([m_as $active ip netns exec frr-ns \
+        vtysh --vty_socket /run/frr/frr-ns -c 'show bgp l2vpn evpn route' | \
+        grep --no-group-separator -A1 00:00:00:01:00:00 | \
+        sed 's/ RD [[0-9.:]]*//g'], [0], [dnl
+ *>  [[2]]:[[0]]:[[48]]:[[00:00:00:01:00:00]]
+                    $host_ip                            0 4210000000 i
+ *>  [[2]]:[[0]]:[[48]]:[[00:00:00:01:00:00]]:[[32]]:[[10.0.0.1]]
+                    $host_ip                            0 4210000000 i
+ *>  [[2]]:[[0]]:[[48]]:[[00:00:00:01:00:00]]:[[128]]:[[10::1]]
+                    $host_ip                            0 4210000000 i
+])
+    OVS_WAIT_FOR_OUTPUT([m_as $standby ip netns exec frr-ns \
+        vtysh --vty_socket /run/frr/frr-ns -c 'show bgp l2vpn evpn route' | \
+        grep -q 00:00:00:01:00:00], [1])
+    priority=$((priority + 10))
+done
+
+# Restore the distributed router before the exact workload cleanup checks.
+check multinode_nbctl --wait=hv \
+    -- clear logical_router_port lr-ls ha_chassis_group \
+    -- ha-chassis-group-del evpn-ha
+m_wait_row_count Port_Binding 0 logical_port=cr-lr-ls
+for gw in ovn-gw-1 ovn-gw-2; do
+    OVS_WAIT_FOR_OUTPUT([m_as $gw bridge fdb show dev lo-10 | \
+        grep -v ' vlan ' | grep -q '^00:00:00:01:00:00 '], [1])
+    OVS_WAIT_FOR_OUTPUT([m_as $gw ip -4 neigh show to 10.0.0.1 dev br-10 nud 
all | \
+        grep -q 'lladdr 00:00:00:01:00:00 '], [1])
+    OVS_WAIT_FOR_OUTPUT([m_as $gw ip -6 neigh show to 10::1 dev br-10 nud all 
| \
+        grep -q 'lladdr 00:00:00:01:00:00 '], [1])
+    OVS_WAIT_FOR_OUTPUT([m_as $gw ip netns exec frr-ns \
+        vtysh --vty_socket /run/frr/frr-ns -c 'show bgp l2vpn evpn route' | \
+        grep -q 00:00:00:01:00:00], [1])
+done
+
 # Remove "workloads" (VIF LSPs) on both chassis.
 check multinode_nbctl --wait=hv lsp-del w1 -- lsp-del w2
 
-- 
2.56.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to