From: Dumitru Ceara <dce...@redhat.com>

When monitoring and updating (Linux) neighbor tables through Netlink,
the interfaces in question are identified by if-index.  It's more
CMS-friendly to allow OVN to be configured to track interfaces by name.

In order to achieve that, we introduce a new host-if-monitor module
which tracks the if-index <-> if-name mapping for relevant interfaces.

NOTE: OVS has the lib/if-notifier.[ch] module already.  However, that is
not really useful for the OVN use case because it doesn't really pass
the struct rtnetlink_change message to its registered callbacks.

Signed-off-by: Dumitru Ceara <dce...@redhat.com>
---
 controller/automake.mk            |   5 +-
 controller/host-if-monitor-stub.c |  43 +++++++++
 controller/host-if-monitor.c      | 150 ++++++++++++++++++++++++++++++
 controller/host-if-monitor.h      |  30 ++++++
 4 files changed, 227 insertions(+), 1 deletion(-)
 create mode 100644 controller/host-if-monitor-stub.c
 create mode 100644 controller/host-if-monitor.c
 create mode 100644 controller/host-if-monitor.h

diff --git a/controller/automake.mk b/controller/automake.mk
index 6af6ee2a9..6eca5e8ed 100644
--- a/controller/automake.mk
+++ b/controller/automake.mk
@@ -64,10 +64,12 @@ controller_ovn_controller_SOURCES = \
        controller/route.c \
        controller/garp_rarp.h \
        controller/garp_rarp.c \
-       controller/neighbor-table-notify.h
+       controller/neighbor-table-notify.h \
+       controller/host-if-monitor.h
 
 if HAVE_NETLINK
 controller_ovn_controller_SOURCES += \
+       controller/host-if-monitor.c \
        controller/neighbor-exchange-netlink.h \
        controller/neighbor-exchange-netlink.c \
        controller/neighbor-table-notify.c \
@@ -77,6 +79,7 @@ controller_ovn_controller_SOURCES += \
        controller/route-table-notify.c
 else
 controller_ovn_controller_SOURCES += \
+       controller/host-if-monitor-stub.c \
        controller/neighbor-table-notify-stub.c \
        controller/route-exchange-stub.c \
        controller/route-table-notify-stub.c
diff --git a/controller/host-if-monitor-stub.c 
b/controller/host-if-monitor-stub.c
new file mode 100644
index 000000000..b174cf996
--- /dev/null
+++ b/controller/host-if-monitor-stub.c
@@ -0,0 +1,43 @@
+/* Copyright (c) 2025, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include "openvswitch/compiler.h"
+#include "host-if-monitor.h"
+
+void
+host_if_monitor_wait(void)
+{
+}
+
+bool
+host_if_monitor_run(void)
+{
+    return false;
+}
+
+void
+host_if_monitor_update_watches(const struct sset *if_names OVS_UNUSED)
+{
+}
+
+int32_t
+host_if_monitor_ifname_toindex(const char *if_name OVS_UNUSED)
+{
+    return 0;
+}
diff --git a/controller/host-if-monitor.c b/controller/host-if-monitor.c
new file mode 100644
index 000000000..26edb03d0
--- /dev/null
+++ b/controller/host-if-monitor.c
@@ -0,0 +1,150 @@
+/* Copyright (c) 2025, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include "lib/rtnetlink.h"
+#include "lib/simap.h"
+#include "openvswitch/vlog.h"
+
+#include "host-if-monitor.h"
+
+VLOG_DEFINE_THIS_MODULE(host_if_monitor);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+
+struct host_if_monitor {
+    struct nln_notifier *link_notifier;
+
+    struct sset watched_interfaces;
+    struct simap ifname_to_ifindex;
+
+    bool changes_detected;
+};
+
+static struct host_if_monitor monitor = (struct host_if_monitor) {
+    .link_notifier = NULL,
+    .watched_interfaces = SSET_INITIALIZER(&monitor.watched_interfaces),
+    .ifname_to_ifindex = SIMAP_INITIALIZER(&monitor.ifname_to_ifindex),
+    .changes_detected = false,
+};
+
+static void if_notifier_cb(const struct rtnetlink_change *, void *aux);
+
+void
+host_if_monitor_wait(void)
+{
+    rtnetlink_wait();
+}
+
+bool
+host_if_monitor_run(void)
+{
+    monitor.changes_detected = false;
+
+    /* If any relevant interface if-index <-> if-name mapping changes are
+     * dected, monitor.changes_detected will be updated accordingly by the
+     * if_notifier_cb(). */
+    rtnetlink_run();
+
+    return monitor.changes_detected;
+}
+
+void
+host_if_monitor_update_watches(const struct sset *if_names)
+{
+    struct sset new_if_names = SSET_INITIALIZER(&new_if_names);
+    const char *if_name;
+
+    /* The notifier only triggers the callback on interface updates.
+     * For newly added ones we need to fetch the initial if_index ourselves.
+     */
+    SSET_FOR_EACH (if_name, if_names) {
+        if (!sset_contains(&monitor.watched_interfaces, if_name)) {
+            sset_add(&new_if_names, if_name);
+        }
+    }
+
+    if (!sset_equals(&monitor.watched_interfaces, if_names)) {
+        sset_destroy(&monitor.watched_interfaces);
+        sset_clone(&monitor.watched_interfaces, if_names);
+    }
+
+    if (!sset_is_empty(&monitor.watched_interfaces)) {
+        if (!monitor.link_notifier) {
+            VLOG_INFO_RL(&rl, "Enabling host interface monitor");
+            monitor.link_notifier =
+                rtnetlink_notifier_create(if_notifier_cb, &monitor);
+        }
+        /* Get initial state for new interfaces.
+         *
+         * NOTE: it's important that we have the initial state (if-index) for
+         * newly watched interfaces because of two reasons:
+         * - we need to be able to reconcile and preserve still valid learned
+         *   remote FDB entries and remote VTEPs
+         * - the if_notifier_cb is called only on updates of interfaces
+         *   therefore if existing interfaces don't change the notifier
+         *   callback is not called.
+         */
+        SSET_FOR_EACH (if_name, &new_if_names) {
+            simap_put(&monitor.ifname_to_ifindex, if_name,
+                      if_nametoindex(if_name));
+        }
+    } else {
+        if (monitor.link_notifier) {
+            VLOG_INFO_RL(&rl, "Disabling host interface monitor");
+            rtnetlink_notifier_destroy(monitor.link_notifier);
+            monitor.link_notifier = NULL;
+        }
+    }
+
+    sset_destroy(&new_if_names);
+}
+
+int32_t
+host_if_monitor_ifname_toindex(const char *if_name)
+{
+    return simap_get(&monitor.ifname_to_ifindex, if_name);
+}
+
+static void
+if_notifier_cb(const struct rtnetlink_change *change, void *aux OVS_UNUSED)
+{
+    if (!change || change->irrelevant) {
+        return;
+    }
+
+    switch (change->nlmsg_type) {
+    case RTM_NEWLINK:
+        if ((change->ifi_flags & IFF_UP)
+            && sset_find(&monitor.watched_interfaces, change->ifname)) {
+            simap_put(&monitor.ifname_to_ifindex,
+                      change->ifname, change->if_index);
+            monitor.changes_detected = true;
+        }
+        break;
+    case RTM_DELLINK:
+        if (sset_find(&monitor.watched_interfaces, change->ifname)) {
+            simap_find_and_delete(&monitor.ifname_to_ifindex, change->ifname);
+            monitor.changes_detected = true;
+        }
+        break;
+    default:
+        break;
+    }
+}
diff --git a/controller/host-if-monitor.h b/controller/host-if-monitor.h
new file mode 100644
index 000000000..a41c5869c
--- /dev/null
+++ b/controller/host-if-monitor.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2025, Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HOST_IF_MONITOR_H
+#define HOST_IF_MONITOR_H 1
+
+#include <stdint.h>
+#include "lib/sset.h"
+#include "lib/smap.h"
+
+void host_if_monitor_wait(void);
+bool host_if_monitor_run(void);
+
+void host_if_monitor_update_watches(const struct sset *if_names);
+
+int32_t host_if_monitor_ifname_toindex(const char *if_name);
+
+#endif /* HOST_IF_MONITOR_H */
-- 
2.50.0

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

Reply via email to