Hi Ales, I have one minor finding below.
On Thu, Oct 8, 2026 at 9:01 AM Ales Musil via dev
<[email protected]> wrote:
>
> The FDB entries would be cleaned up when the port or datapath
> was removed. However, that wasn't enough as there might have been
> stale entries when the port changed address from "unknown" or would
> be disabled. Make sure we do a proper cleanup for those changes too.
>
> Fixes: 679d3550303a ("northd: Cleanup stale FDB entries.")
> Reported-at: https://redhat.atlassian.net/browse/FDP-4432
> Signed-off-by: Ales Musil <[email protected]>
> ---
> northd/en-northd.c | 29 ++++++++++++++--------------
> northd/northd.c | 32 ++++++++++++++++++++-----------
> northd/northd.h | 2 ++
> tests/ovn-northd.at | 46 +++++++++++++++++++++++++++++++++++++--------
> 4 files changed, 75 insertions(+), 34 deletions(-)
>
> diff --git a/northd/en-northd.c b/northd/en-northd.c
> index 480dc61ca..c8c57ea5b 100644
> --- a/northd/en-northd.c
> +++ b/northd/en-northd.c
> @@ -694,32 +694,31 @@ northd_sb_fdb_change_handler(struct engine_node *node,
> void *data)
> const struct sbrec_fdb_table *sbrec_fdb_table =
> EN_OVSDB_GET(engine_get_input("SB_fdb", node));
>
> + struct vector to_remove =
> + VECTOR_EMPTY_INITIALIZER(struct sbrec_fdb_table *);
The vector actually contains pointers to struct sbrec_fdb, not
sbrec_fdb_table. The code works because pointers are all the same
size, but it's probably best to use the correct expected struct
pointer here.
> +
> /* check if changed rows are stale and delete them */
> - const struct sbrec_fdb *fdb_e, *fdb_prev_del = NULL;
> + const struct sbrec_fdb *fdb_e;
> SBREC_FDB_TABLE_FOR_EACH_TRACKED (fdb_e, sbrec_fdb_table) {
> if (sbrec_fdb_is_deleted(fdb_e)) {
> continue;
> }
>
> - if (fdb_prev_del) {
> - sbrec_fdb_delete(fdb_prev_del);
> - }
> -
> - fdb_prev_del = fdb_e;
> - struct ovn_datapath *od
> - = ovn_datapath_find_by_key(&nd->ls_datapaths.datapaths,
> - fdb_e->dp_key);
> - if (od) {
> - if (ovn_tnlid_present(&od->port_tnlids, fdb_e->port_key)) {
> - fdb_prev_del = NULL;
> - }
> + struct ovn_datapath *od =
> + ovn_datapath_find_by_key(&nd->ls_datapaths.datapaths,
> + fdb_e->dp_key);
> + if (!od || ovn_datapath_is_stale(od) ||
> + !ovn_tnlid_present(&od->fdb_ports_tnlids, fdb_e->port_key)) {
> + vector_push(&to_remove, &fdb_e);
> }
> }
>
> - if (fdb_prev_del) {
> - sbrec_fdb_delete(fdb_prev_del);
> + VECTOR_FOR_EACH (&to_remove, fdb_e) {
> + sbrec_fdb_delete(fdb_e);
> }
>
> + vector_destroy(&to_remove);
> +
> return EN_HANDLED_UNCHANGED;
> }
>
> diff --git a/northd/northd.c b/northd/northd.c
> index e6a2333b5..7c4cb5a62 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -617,6 +617,7 @@ ovn_datapath_create(struct hmap *datapaths, const struct
> uuid *key,
> od->sdp = sdp;
> od->nbs = nbs;
> od->nbr = nbr;
> + hmap_init(&od->fdb_ports_tnlids);
> hmap_init(&od->port_tnlids);
> od->port_key_hint = 0;
> hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
> @@ -653,6 +654,7 @@ ovn_datapath_destroy(struct ovn_datapath *od)
> /* Don't remove od->list. It is used within build_datapaths() as a
> * private list and once we've exited that function it is not safe to
> * use it. */
> + ovn_destroy_tnlids(&od->fdb_ports_tnlids);
> ovn_destroy_tnlids(&od->port_tnlids);
> destroy_ipam_info(&od->ipam_info);
> vector_destroy(&od->router_ports);
> @@ -1163,6 +1165,7 @@ ovn_port_cleanup(struct ovn_port *port)
> if (port->tunnel_key) {
> ovs_assert(port->od);
> ovn_free_tnlid(&port->od->port_tnlids, port->tunnel_key);
> + ovn_free_tnlid(&port->od->fdb_ports_tnlids, port->tunnel_key);
> port->tunnel_key = 0;
> }
> for (int i = 0; i < port->n_lsp_addrs; i++) {
> @@ -3135,16 +3138,10 @@ cleanup_stale_fdb_entries(const struct
> sbrec_fdb_table *sbrec_fdb_table,
> {
> const struct sbrec_fdb *fdb_e;
> SBREC_FDB_TABLE_FOR_EACH_SAFE (fdb_e, sbrec_fdb_table) {
> - bool delete = true;
> - struct ovn_datapath *od
> - = ovn_datapath_find_by_key(ls_datapaths, fdb_e->dp_key);
> - if (od) {
> - if (ovn_tnlid_present(&od->port_tnlids, fdb_e->port_key)) {
> - delete = false;
> - }
> - }
> -
> - if (delete) {
> + struct ovn_datapath *od =
> + ovn_datapath_find_by_key(ls_datapaths, fdb_e->dp_key);
> + if (!od || ovn_datapath_is_stale(od) ||
> + !ovn_tnlid_present(&od->fdb_ports_tnlids, fdb_e->port_key)) {
> sbrec_fdb_delete(fdb_e);
> }
> }
> @@ -4369,6 +4366,12 @@ ovn_port_add_tnlid(struct ovn_port *op, uint32_t
> tunnel_key)
> if (tunnel_key > op->od->port_key_hint) {
> op->od->port_key_hint = tunnel_key;
> }
> +
> + /* Track the assigned tunnel_key for enabled LSP
> + * with unknown address. */
> + if (op->nbsp && lsp_is_enabled(op->nbsp) && op->has_unknown) {
> + ovs_assert(ovn_add_tnlid(&op->od->fdb_ports_tnlids, tunnel_key));
> + }
> }
> return added;
> }
> @@ -4421,6 +4424,11 @@ ovn_port_allocate_key(struct ovn_port *op)
> if (!op->tunnel_key) {
> return false;
> }
> +
> + if (op->nbsp && lsp_is_enabled(op->nbsp) && op->has_unknown) {
> + ovs_assert(ovn_add_tnlid(&op->od->fdb_ports_tnlids,
> + op->tunnel_key));
> + }
> }
> return true;
> }
> @@ -5091,7 +5099,9 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> }
> add_op_to_northd_tracked_ports(&trk_lsps->updated, op);
>
> - if (old_tunnel_key != op->tunnel_key) {
> + if (old_tunnel_key != op->tunnel_key ||
> + !lsp_is_enabled(op->nbsp) ||
> + !op->has_unknown) {
> delete_fdb_entries(ni->sbrec_fdb_by_dp_and_port,
> od->tunnel_key, old_tunnel_key);
> }
> diff --git a/northd/northd.h b/northd/northd.h
> index a2b8a0c93..3d1fd2d31 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -421,6 +421,8 @@ struct ovn_datapath {
> struct vector router_ports; /* Vector of struct ovn_port *. */
> struct vector switch_ports; /* Vector of struct ovn_port * of
> * type 'switch'. */
> + struct hmap fdb_ports_tnlids; /* Tunnel keys for enabled LSP with
> + * unknown address. */
> struct hmap port_tnlids;
> uint32_t port_key_hint;
>
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 6866068fa..ec04d4d67 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -5344,28 +5344,38 @@ AT_SETUP([FDB cleanup])
> ovn_start
>
> check ovn-nbctl ls-add sw0
> -check ovn-nbctl lsp-add sw0 sw0-p1
> -check ovn-nbctl lsp-add sw0 sw0-p2
> -check ovn-nbctl lsp-add sw0 sw0-p3
> +check ovn-nbctl lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 unknown
> +check ovn-nbctl lsp-add sw0 sw0-p2 -- lsp-set-addresses sw0-p2 unknown
> +check ovn-nbctl lsp-add sw0 sw0-p3 -- lsp-set-addresses sw0-p3 unknown \
> + -- set Logical_Switch_Port sw0-p3 enabled=false
> +check ovn-nbctl lsp-add sw0 sw0-p4
>
> check ovn-nbctl ls-add sw1
> -check ovn-nbctl lsp-add sw1 sw1-p1
> -check ovn-nbctl lsp-add sw1 sw1-p2
> -check ovn-nbctl --wait=sb lsp-add sw1 sw1-p3
> +check ovn-nbctl lsp-add sw1 sw1-p1 -- lsp-set-addresses sw1-p1 unknown
> +check ovn-nbctl --wait=sb sync
>
> sw0_key=$(fetch_column datapath_binding tunnel_key external_ids:name=sw0)
> sw1_key=$(fetch_column datapath_binding tunnel_key external_ids:name=sw1)
> sw0p1_key=$(fetch_column port_binding tunnel_key logical_port=sw0-p1)
> sw0p2_key=$(fetch_column port_binding tunnel_key logical_port=sw0-p2)
> +sw0p3_key=$(fetch_column port_binding tunnel_key logical_port=sw0-p3)
> +sw0p4_key=$(fetch_column port_binding tunnel_key logical_port=sw0-p4)
> sw1p1_key=$(fetch_column port_binding tunnel_key logical_port=sw1-p1)
>
> check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:01" dp_key=$sw0_key
> port_key=$sw0p1_key
> check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:02" dp_key=$sw0_key
> port_key=$sw0p1_key
> check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:03" dp_key=$sw0_key
> port_key=$sw0p2_key
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:04" dp_key=$sw0_key
> port_key=$sw0p3_key
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:05" dp_key=$sw0_key
> port_key=$sw0p4_key
> check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:01\:01" dp_key=$sw1_key
> port_key=$sw1p1_key
> check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:01\:02" dp_key=$sw1_key
> port_key=$sw1p1_key
> check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:01\:03" dp_key=$sw1_key
> port_key=$sw1p1_key
>
> +# Disabled port should clear FDB.
> +wait_row_count FDB 0 dp_key=$sw0_key port_key=$sw0p3_key
> +# Port without "unknown address" should clear FDB.
> +wait_row_count FDB 0 dp_key=$sw0_key port_key=$sw0p4_key
> +
> wait_row_count FDB 6
>
> AT_CHECK([ovn-sbctl create fdb mac="00\:00\:00\:00\:01\:03" dp_key=$sw1_key
> port_key=10], [1], [ignore], [ignore])
> @@ -5383,12 +5393,32 @@ check ovn-nbctl lsp-del sw0-p1
> wait_row_count FDB 1
>
> check_column '00:00:00:00:00:03' FDB mac
> -ovn-sbctl list fdb
> +ovn-sbctl list FDB
>
> check_column $sw0_key FDB dp_key
> check_column $sw0p2_key FDB port_key
>
> -check ovn-nbctl --wait=sb lsp-add sw0 sw0-p1
> +check ovn-nbctl --wait=sb lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1
> unknown
> +sw0p1_key=$(fetch_column port_binding tunnel_key logical_port=sw0-p1)
> +wait_row_count FDB 1
> +
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:01" dp_key=$sw0_key
> port_key=$sw0p1_key
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:02" dp_key=$sw0_key
> port_key=$sw0p1_key
> +wait_row_count FDB 3
> +
> +# Disabling clears FDB entries.
> +check ovn-nbctl --wait=sb set Logical_Switch_Port sw0-p1 enabled=false
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:01" dp_key=$sw0_key
> port_key=$sw0p1_key
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:02" dp_key=$sw0_key
> port_key=$sw0p1_key
> +wait_row_count FDB 1
> +
> +check ovn-nbctl --wait=sb set Logical_Switch_Port sw0-p1 enabled=true
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:01" dp_key=$sw0_key
> port_key=$sw0p1_key
> +check_uuid ovn-sbctl create FDB mac="00\:00\:00\:00\:00\:02" dp_key=$sw0_key
> port_key=$sw0p1_key
> +wait_row_count FDB 3
> +
> +# Removing unknown clears FDB entries.
> +check ovn-nbctl --wait=sb lsp-set-addresses sw0-p1 "00:00:00:00:00:10
> 192.168.100.10"
> wait_row_count FDB 1
>
> check ovn-nbctl lsp-del sw0-p2
> --
> 2.55.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev