From 13f83149e1a96d115d3959197eff4c6106adcb6c Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 9 Feb 2026 13:37:52 +0100 Subject: [PATCH] core: add nexthop id reservation function in the netns When an IP configuration method (e.g. NDisc) wants to use a nexthop, it needs to allocate a free id. Add functions to keep track of the used ids in the namespace so that we avoid collisions. --- src/core/nm-netns.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ src/core/nm-netns.h | 7 ++++ 2 files changed, 86 insertions(+) diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c index faae72670d..1cf9d4d5fb 100644 --- a/src/core/nm-netns.c +++ b/src/core/nm-netns.c @@ -83,6 +83,11 @@ typedef struct { * by IP address. */ GHashTable *watcher_ip_data_idx; + /* Tracks reserved nexthop IDs. Maps guint32 id -> gconstpointer tag. + * This is used to avoid nexthop ID collisions across different + * interfaces/ndisc instances within the same network namespace. */ + GHashTable *nexthop_id_reserved; + CList l3cfg_signal_pending_lst_head; GSource *signal_pending_idle_source; } NMNetnsPrivate; @@ -1521,6 +1526,76 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps /*****************************************************************************/ +gboolean +nm_netns_nexthop_id_is_reserved(NMNetns *self, guint32 id) +{ + NMNetnsPrivate *priv; + + g_return_val_if_fail(NM_IS_NETNS(self), FALSE); + nm_assert(id != 0); + + priv = NM_NETNS_GET_PRIVATE(self); + return g_hash_table_contains(priv->nexthop_id_reserved, GUINT_TO_POINTER(id)); +} + +gboolean +nm_netns_nexthop_id_reserve(NMNetns *self, guint32 id, gconstpointer tag) +{ + NMNetnsPrivate *priv; + gpointer existing_tag; + + g_return_val_if_fail(NM_IS_NETNS(self), FALSE); + nm_assert(id != 0); + nm_assert(tag); + + priv = NM_NETNS_GET_PRIVATE(self); + + if (g_hash_table_lookup_extended(priv->nexthop_id_reserved, + GUINT_TO_POINTER(id), + NULL, + &existing_tag)) { + /* The ID is already reserved. If it's by the same tag, it's + * idempotent; otherwise it's a collision. */ + return existing_tag == tag; + } + + g_hash_table_insert(priv->nexthop_id_reserved, GUINT_TO_POINTER(id), (gpointer) tag); + return TRUE; +} + +void +nm_netns_nexthop_id_release(NMNetns *self, guint32 id) +{ + NMNetnsPrivate *priv; + + g_return_if_fail(NM_IS_NETNS(self)); + nm_assert(id != 0); + + priv = NM_NETNS_GET_PRIVATE(self); + g_hash_table_remove(priv->nexthop_id_reserved, GUINT_TO_POINTER(id)); +} + +void +nm_netns_nexthop_id_release_all(NMNetns *self, gconstpointer tag) +{ + NMNetnsPrivate *priv; + GHashTableIter iter; + gpointer value; + + g_return_if_fail(NM_IS_NETNS(self)); + nm_assert(tag); + + priv = NM_NETNS_GET_PRIVATE(self); + + g_hash_table_iter_init(&iter, priv->nexthop_id_reserved); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + if (value == tag) + g_hash_table_iter_remove(&iter); + } +} + +/*****************************************************************************/ + static void nm_netns_init(NMNetns *self) { @@ -1545,6 +1620,8 @@ nm_netns_init(NMNetns *self) (GDestroyNotify) _watcher_by_tag_destroy, NULL); priv->watcher_ip_data_idx = g_hash_table_new(_watcher_ip_data_hash, _watcher_ip_data_equal); + + priv->nexthop_id_reserved = g_hash_table_new(nm_direct_hash, NULL); } static void @@ -1637,6 +1714,8 @@ dispose(GObject *object) nm_clear_pointer(&priv->watcher_by_tag_idx, g_hash_table_destroy); nm_clear_pointer(&priv->watcher_ip_data_idx, g_hash_table_destroy); + nm_clear_pointer(&priv->nexthop_id_reserved, g_hash_table_destroy); + nm_clear_g_source_inst(&priv->signal_pending_idle_source); if (priv->platform) diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h index e32d5680aa..c2414c7dfd 100644 --- a/src/core/nm-netns.h +++ b/src/core/nm-netns.h @@ -109,4 +109,11 @@ void nm_netns_watcher_add(NMNetns *self, void nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag); void nm_netns_watcher_remove_dirty(NMNetns *self, gconstpointer tag); +/*****************************************************************************/ + +gboolean nm_netns_nexthop_id_is_reserved(NMNetns *self, guint32 id); +gboolean nm_netns_nexthop_id_reserve(NMNetns *self, guint32 id, gconstpointer tag); +void nm_netns_nexthop_id_release(NMNetns *self, guint32 id); +void nm_netns_nexthop_id_release_all(NMNetns *self, gconstpointer tag); + #endif /* __NM_NETNS_H__ */