about summary refs log tree commit diff
path: root/src/core/nm-l3cfg.c
diff options
context:
space:
mode:
authorJeremy Bicha <jeremy.bicha@canonical.com>2022-02-22 16:51:45 -0500
committerJeremy Bicha <jeremy.bicha@canonical.com>2022-02-22 16:51:45 -0500
commit9d53567404b8f256e36619ee9345cef6dfb59f76 (patch)
tree8c760223d10c83710d45d5c2819f39f121e3d9f0 /src/core/nm-l3cfg.c
parentda4d2b990df5b0119d1dffd76ff68141517d6136 (diff)
parent2f94dba7385fd0e0ef19a06eb4a2fcf6c43d7946 (diff)
Merge tag 'upstream/1.35.91' into ubuntu/master
Upstream version 1.35.91
Diffstat (limited to 'src/core/nm-l3cfg.c')
-rw-r--r--src/core/nm-l3cfg.c263
1 files changed, 229 insertions, 34 deletions
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index aed4cf05..bcbe140c 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -12,6 +12,7 @@
 #include "libnm-glib-aux/nm-time-utils.h"
 #include "libnm-platform/nm-platform.h"
 #include "libnm-platform/nmp-object.h"
+#include "libnm-platform/nmp-route-manager.h"
 #include "nm-netns.h"
 #include "n-acd/src/n-acd.h"
 #include "nm-l3-ipv4ll.h"
@@ -410,6 +411,92 @@ static NM_UTILS_LOOKUP_DEFINE(_l3_acd_addr_state_to_string,
                                                    "external-removed"),
                               NM_UTILS_LOOKUP_ITEM(NM_L3_ACD_ADDR_STATE_USED, "used"), );
 
+static gboolean
+_obj_is_route_nodev(const NMPObject *obj)
+{
+    gboolean has_ifindex;
+
+    nm_assert(obj);
+
+    has_ifindex = (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex > 0);
+
+    nm_assert(has_ifindex
+              == !(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj),
+                             NMP_OBJECT_TYPE_IP4_ROUTE,
+                             NMP_OBJECT_TYPE_IP6_ROUTE)
+                   && nm_platform_route_type_is_nodev(nm_platform_route_type_uncoerce(
+                       NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced))));
+
+    return !has_ifindex;
+}
+
+/*****************************************************************************/
+
+NMIPConfig *
+nm_l3cfg_ipconfig_get(NML3Cfg *self, int addr_family)
+{
+    g_return_val_if_fail(NM_IS_L3CFG(self), NULL);
+    nm_assert_addr_family(addr_family);
+
+    return self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)];
+}
+
+static void
+_ipconfig_toggle_notify(gpointer data, GObject *object, gboolean is_last_ref)
+{
+    NML3Cfg    *self     = NM_L3CFG(data);
+    NMIPConfig *ipconfig = NM_IP_CONFIG(object);
+
+    if (!is_last_ref) {
+        /* This happens while we take another ref below. Ignore the signal. */
+        nm_assert(!NM_IN_SET(ipconfig, self->priv.p->ipconfig_4, self->priv.p->ipconfig_6));
+        return;
+    }
+
+    if (ipconfig == self->priv.p->ipconfig_4)
+        self->priv.p->ipconfig_4 = NULL;
+    else {
+        nm_assert(ipconfig == self->priv.p->ipconfig_6);
+        self->priv.p->ipconfig_6 = NULL;
+    }
+
+    /* We take a second reference to keep the instance alive, while also removing the
+     * toggle ref. This will notify the function again, but we will ignore that. */
+    g_object_ref(ipconfig);
+
+    g_object_remove_toggle_ref(G_OBJECT(ipconfig), _ipconfig_toggle_notify, self);
+
+    /* pass on the reference, and unexport on idle. */
+    nm_ip_config_take_and_unexport_on_idle(g_steal_pointer(&ipconfig));
+}
+
+NMIPConfig *
+nm_l3cfg_ipconfig_acquire(NML3Cfg *self, int addr_family)
+{
+    NMIPConfig *ipconfig;
+
+    g_return_val_if_fail(NM_IS_L3CFG(self), NULL);
+    nm_assert_addr_family(addr_family);
+
+    ipconfig = self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)];
+
+    if (ipconfig)
+        return g_object_ref(ipconfig);
+
+    ipconfig = nm_ip_config_new(addr_family, self);
+
+    self->priv.p->ipconfig_x[NM_IS_IPv4(addr_family)] = ipconfig;
+
+    /* The ipconfig keeps self alive. We use a toggle reference
+     * to avoid a cycle. But we anyway wouldn't want a strong reference,
+     * because the user releases the instance by unrefing it, and we
+     * notice that via the weak reference. */
+    g_object_add_toggle_ref(G_OBJECT(ipconfig), _ipconfig_toggle_notify, self);
+
+    /* We keep the toggle reference, and return the other reference to the caller. */
+    return g_steal_pointer(&ipconfig);
+}
+
 /*****************************************************************************/
 
 NMIPConfig *
@@ -952,6 +1039,11 @@ _obj_states_update_all(NML3Cfg *self)
                                              self->priv.p->combined_l3cd_commited,
                                              &obj,
                                              obj_type) {
+            if (_obj_is_route_nodev(obj)) {
+                /* this is a nodev route. We don't track an obj-state for this. */
+                continue;
+            }
+
             obj_state = g_hash_table_lookup(self->priv.p->obj_state_hash, &obj);
             if (!obj_state) {
                 obj_state =
@@ -1008,18 +1100,11 @@ typedef struct {
 } ObjStatesSyncFilterData;
 
 static gboolean
-_obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer user_data)
+_obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType commit_type)
 {
-    char                           sbuf[sizeof(_nm_utils_to_string_buffer)];
-    const NMPObject               *obj              = o;
-    const ObjStatesSyncFilterData *sync_filter_data = user_data;
-    NMPObjectType                  obj_type;
-    ObjStateData                  *obj_state;
-    NML3Cfg                       *self;
-
-    nm_assert(sync_filter_data);
-    nm_assert(NM_IS_L3CFG(sync_filter_data->self));
-    self = sync_filter_data->self;
+    char          sbuf[sizeof(_nm_utils_to_string_buffer)];
+    NMPObjectType obj_type;
+    ObjStateData *obj_state;
 
     obj_type = NMP_OBJECT_GET_TYPE(obj);
 
@@ -1027,14 +1112,14 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer
         && NMP_OBJECT_CAST_IP4_ADDRESS(obj)->a_acd_not_ready)
         return FALSE;
 
-    obj_state = g_hash_table_lookup(sync_filter_data->self->priv.p->obj_state_hash, &obj);
+    obj_state = g_hash_table_lookup(self->priv.p->obj_state_hash, &obj);
 
-    nm_assert_obj_state(sync_filter_data->self, obj_state);
+    nm_assert_obj_state(self, obj_state);
     nm_assert(obj_state->obj == obj);
     nm_assert(c_list_is_empty(&obj_state->os_zombie_lst));
 
     if (!obj_state->os_nm_configured) {
-        if (sync_filter_data->commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME
+        if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME
             && !_obj_state_data_get_assume_config_once(obj_state))
             return FALSE;
 
@@ -1051,13 +1136,78 @@ _obj_states_sync_filter(/* const NMDedupMultiObj * */ gconstpointer o, gpointer
         return TRUE;
     }
 
-    if (!obj_state->os_plobj && sync_filter_data->commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY
+    if (!obj_state->os_plobj && commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY
         && !nmp_object_get_force_commit(obj))
         return FALSE;
 
     return TRUE;
 }
 
+static gboolean
+_obj_states_sync_filter_predicate(gconstpointer o, gpointer user_data)
+{
+    const NMPObject               *obj              = o;
+    const ObjStatesSyncFilterData *sync_filter_data = user_data;
+
+    return _obj_states_sync_filter(sync_filter_data->self, obj, sync_filter_data->commit_type);
+}
+
+static GPtrArray *
+_commit_collect_addresses(NML3Cfg *self, int addr_family, NML3CfgCommitType commit_type)
+{
+    const int                     IS_IPv4 = NM_IS_IPv4(addr_family);
+    const NMDedupMultiHeadEntry  *head_entry;
+    const ObjStatesSyncFilterData sync_filter_data = {
+        .self        = self,
+        .commit_type = commit_type,
+    };
+
+    head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
+                                               NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4));
+    return nm_dedup_multi_objs_to_ptr_array_head(head_entry,
+                                                 _obj_states_sync_filter_predicate,
+                                                 (gpointer) &sync_filter_data);
+}
+
+static void
+_commit_collect_routes(NML3Cfg          *self,
+                       int               addr_family,
+                       NML3CfgCommitType commit_type,
+                       GPtrArray       **routes,
+                       GPtrArray       **routes_nodev)
+{
+    const int                    IS_IPv4 = NM_IS_IPv4(addr_family);
+    const NMDedupMultiHeadEntry *head_entry;
+    const NMDedupMultiEntry     *entry;
+
+    nm_assert(routes && !*routes);
+    nm_assert(routes_nodev && !*routes_nodev);
+
+    head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
+                                               NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4));
+
+    if (!head_entry)
+        return;
+
+    c_list_for_each_entry (entry, &head_entry->lst_entries_head, lst_entries) {
+        const NMPObject *obj = entry->obj;
+        GPtrArray      **r;
+
+        if (_obj_is_route_nodev(obj))
+            r = routes_nodev;
+        else {
+            if (!_obj_states_sync_filter(self, obj, commit_type))
+                continue;
+            r = routes;
+        }
+
+        if (!*r)
+            *r = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref);
+
+        g_ptr_array_add(*r, (gpointer) nmp_object_ref(obj));
+    }
+}
+
 static void
 _obj_state_zombie_lst_get_prune_lists(NML3Cfg    *self,
                                       int         addr_family,
@@ -2386,9 +2536,9 @@ handle_init:
             nm_utils_get_monotonic_timestamp_msec_cached(p_now_msec);
 
             if (acd_data->info.state == NM_L3_ACD_ADDR_STATE_PROBING) {
-                if (acd_data->probing_timestamp_msec + ACD_WAIT_PROBING_EXTRA_TIME_MSEC
-                        + ACD_WAIT_PROBING_EXTRA_TIME2_MSEC
-                    >= (*p_now_msec)) {
+                if ((*p_now_msec) > acd_data->probing_timestamp_msec
+                                        + ACD_WAIT_PROBING_EXTRA_TIME_MSEC
+                                        + ACD_WAIT_PROBING_EXTRA_TIME2_MSEC) {
                     /* hm. We failed to create a new probe too long. Something is really wrong
                      * internally, but let's ignore the issue and assume the address is good. What
                      * else would we do? Assume the address is USED? */
@@ -3351,6 +3501,53 @@ nm_l3cfg_remove_config_all_dirty(NML3Cfg *self, gconstpointer tag)
 
 /*****************************************************************************/
 
+#define _NODEV_ROUTES_TAG(self, IS_IPv4) ((gconstpointer) (&(&(self)->priv.route_manager)[IS_IPv4]))
+
+static gboolean
+_nodev_routes_untrack(NML3Cfg *self, int addr_family)
+{
+    return nmp_route_manager_untrack_all(self->priv.route_manager,
+                                         _NODEV_ROUTES_TAG(self, NM_IS_IPv4(addr_family)),
+                                         FALSE,
+                                         TRUE);
+}
+
+static void
+_nodev_routes_sync(NML3Cfg          *self,
+                   int               addr_family,
+                   NML3CfgCommitType commit_type,
+                   GPtrArray        *routes_nodev)
+{
+    const int           IS_IPv4  = NM_IS_IPv4(addr_family);
+    const NMPObjectType obj_type = NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4);
+    guint               i;
+    gboolean            changed = FALSE;
+
+    if (!routes_nodev)
+        goto out_clear;
+
+    for (i = 0; i < routes_nodev->len; i++) {
+        const NMPObject *obj = routes_nodev->pdata[i];
+
+        if (nmp_route_manager_track(self->priv.route_manager,
+                                    obj_type,
+                                    NMP_OBJECT_CAST_IP_ROUTE(obj),
+                                    1,
+                                    _NODEV_ROUTES_TAG(self, IS_IPv4),
+                                    NULL))
+            changed = TRUE;
+    }
+
+out_clear:
+    if (_nodev_routes_untrack(self, addr_family))
+        changed = TRUE;
+
+    if (changed || commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY)
+        nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4), FALSE);
+}
+
+/*****************************************************************************/
+
 typedef struct {
     NML3Cfg      *self;
     gconstpointer tag;
@@ -3789,6 +3986,7 @@ out_prune:
 }
 
 /*****************************************************************************/
+
 static const char *
 ip6_privacy_to_str(NMSettingIP6ConfigPrivacy ip6_privacy)
 {
@@ -4049,6 +4247,7 @@ _l3_commit_one(NML3Cfg              *self,
     const int                    IS_IPv4                            = NM_IS_IPv4(addr_family);
     gs_unref_ptrarray GPtrArray *addresses                          = NULL;
     gs_unref_ptrarray GPtrArray *routes                             = NULL;
+    gs_unref_ptrarray GPtrArray *routes_nodev                       = NULL;
     gs_unref_ptrarray GPtrArray *addresses_prune                    = NULL;
     gs_unref_ptrarray GPtrArray *routes_prune                       = NULL;
     gs_unref_ptrarray GPtrArray *routes_temporary_not_available_arr = NULL;
@@ -4070,23 +4269,9 @@ _l3_commit_one(NML3Cfg              *self,
           _l3_cfg_commit_type_to_string(commit_type, sbuf_commit_type, sizeof(sbuf_commit_type)));
 
     if (self->priv.p->combined_l3cd_commited) {
-        const NMDedupMultiHeadEntry  *head_entry;
-        const ObjStatesSyncFilterData sync_filter_data = {
-            .self        = self,
-            .commit_type = commit_type,
-        };
+        addresses = _commit_collect_addresses(self, addr_family, commit_type);
 
-        head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
-                                                   NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4));
-        addresses  = nm_dedup_multi_objs_to_ptr_array_head(head_entry,
-                                                          _obj_states_sync_filter,
-                                                          (gpointer) &sync_filter_data);
-
-        head_entry = nm_l3_config_data_lookup_objs(self->priv.p->combined_l3cd_commited,
-                                                   NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4));
-        routes     = nm_dedup_multi_objs_to_ptr_array_head(head_entry,
-                                                       _obj_states_sync_filter,
-                                                       (gpointer) &sync_filter_data);
+        _commit_collect_routes(self, addr_family, commit_type, &routes, &routes_nodev);
 
         route_table_sync =
             nm_l3_config_data_get_route_table_sync(self->priv.p->combined_l3cd_commited,
@@ -4124,6 +4309,8 @@ _l3_commit_one(NML3Cfg              *self,
                                 addresses,
                                 addresses_prune);
 
+    _nodev_routes_sync(self, addr_family, commit_type, routes_nodev);
+
     if (!nm_platform_ip_route_sync(self->priv.platform,
                                    addr_family,
                                    self->priv.ifindex,
@@ -4558,6 +4745,8 @@ constructed(GObject *object)
     self->priv.platform = g_object_ref(nm_netns_get_platform(self->priv.netns));
     nm_assert(NM_IS_PLATFORM(self->priv.platform));
 
+    self->priv.route_manager = nmp_route_manager_ref(nm_netns_get_route_manager(self->priv.netns));
+
     _LOGT("created (netns=" NM_HASH_OBFUSCATE_PTR_FMT ")", NM_HASH_OBFUSCATE_PTR(self->priv.netns));
 
     G_OBJECT_CLASS(nm_l3cfg_parent_class)->constructed(object);
@@ -4608,8 +4797,14 @@ finalize(GObject *object)
     nm_assert(c_list_is_empty(&self->priv.p->obj_state_temporary_not_available_lst_head));
     nm_assert(c_list_is_empty(&self->priv.p->obj_state_zombie_lst_head));
 
+    if (_nodev_routes_untrack(self, AF_INET))
+        nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE);
+    if (_nodev_routes_untrack(self, AF_INET6))
+        nmp_route_manager_sync(self->priv.route_manager, NMP_OBJECT_TYPE_IP6_ROUTE, FALSE);
+
     g_clear_object(&self->priv.netns);
     g_clear_object(&self->priv.platform);
+    nm_clear_pointer(&self->priv.route_manager, nmp_route_manager_unref);
 
     nm_clear_l3cd(&self->priv.p->combined_l3cd_merged);
     nm_clear_l3cd(&self->priv.p->combined_l3cd_commited);