about summary refs log tree commit diff
path: root/clients/cloud-setup/nmcs-provider-ec2.c
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2021-01-11 18:07:03 +0100
committerSebastien Bacher <seb128@ubuntu.com>2021-01-11 18:15:57 +0100
commit2567f558da3eb9911678227d4aeb15162e707d89 (patch)
tree13c80b0e091c8ba585b14b966cda998f95f1936d /clients/cloud-setup/nmcs-provider-ec2.c
parent02d99ffd62ede29cffcb45fcbe33d044b7406d3a (diff)
parentf54032b30e1ddd84f10a339e0caf493772e049a7 (diff)
Merge branch 'debian/master' into ubuntu/master
Diffstat (limited to 'clients/cloud-setup/nmcs-provider-ec2.c')
-rw-r--r--clients/cloud-setup/nmcs-provider-ec2.c887
1 files changed, 423 insertions, 464 deletions
diff --git a/clients/cloud-setup/nmcs-provider-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c
index 3b5f6d36..fb82914f 100644
--- a/clients/cloud-setup/nmcs-provider-ec2.c
+++ b/clients/cloud-setup/nmcs-provider-ec2.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: LGPL-2.1+
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "nm-default.h"
 
@@ -10,550 +10,509 @@
 
 #define HTTP_TIMEOUT_MS 3000
 
-#define NM_EC2_HOST               "169.254.169.254"
-#define NM_EC2_BASE               "http://" NM_EC2_HOST
-#define NM_EC2_API_VERSION        "2018-09-24"
-#define NM_EC2_METADATA_URL_BASE  /* $NM_EC2_BASE/$NM_EC2_API_VERSION */ "/meta-data/network/interfaces/macs/"
+#define NM_EC2_HOST              "169.254.169.254"
+#define NM_EC2_BASE              "http://" NM_EC2_HOST
+#define NM_EC2_API_VERSION       "2018-09-24"
+#define NM_EC2_METADATA_URL_BASE /* $NM_EC2_BASE/$NM_EC2_API_VERSION */ \
+    "/meta-data/network/interfaces/macs/"
 
 static const char *
-_ec2_base (void)
+_ec2_base(void)
 {
-	static const char *base_cached = NULL;
-	const char *base;
+    static const char *base_cached = NULL;
+    const char *       base;
 
 again:
-	base = g_atomic_pointer_get (&base_cached);
-	if (G_UNLIKELY (!base)) {
-
-		/* The base URI can be set via environment variable.
-		 * This is mainly for testing, it's not usually supposed to be configured.
-		 * Consider this private API! */
-		base = g_getenv (NMCS_ENV_VARIABLE ("NM_CLOUD_SETUP_EC2_HOST"));
-
-		if (   base
-		    && base[0]
-		    && !strchr (base, '/')) {
-			if (   NM_STR_HAS_PREFIX (base, "http://")
-			    || NM_STR_HAS_PREFIX (base, "https://"))
-				base = g_intern_string (base);
-			else {
-				gs_free char *s = NULL;
-
-				s = g_strconcat ("http://", base, NULL);
-				base = g_intern_string (s);
-			}
-		}
-		if (!base)
-			base = NM_EC2_BASE;
-
-		nm_assert (!NM_STR_HAS_SUFFIX (base, "/"));
-
-		if (!g_atomic_pointer_compare_and_exchange (&base_cached, NULL, base))
-			goto again;
-	}
-
-	return base;
+    base = g_atomic_pointer_get(&base_cached);
+    if (G_UNLIKELY(!base)) {
+        /* The base URI can be set via environment variable.
+         * This is mainly for testing, it's not usually supposed to be configured.
+         * Consider this private API! */
+        base = g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2_HOST"));
+
+        if (base && base[0] && !strchr(base, '/')) {
+            if (NM_STR_HAS_PREFIX(base, "http://") || NM_STR_HAS_PREFIX(base, "https://"))
+                base = g_intern_string(base);
+            else {
+                gs_free char *s = NULL;
+
+                s    = g_strconcat("http://", base, NULL);
+                base = g_intern_string(s);
+            }
+        }
+        if (!base)
+            base = NM_EC2_BASE;
+
+        nm_assert(!NM_STR_HAS_SUFFIX(base, "/"));
+
+        if (!g_atomic_pointer_compare_and_exchange(&base_cached, NULL, base))
+            goto again;
+    }
+
+    return base;
 }
 
-#define _ec2_uri_concat(...)     nmcs_utils_uri_build_concat (_ec2_base (), __VA_ARGS__)
-#define _ec2_uri_interfaces(...) _ec2_uri_concat (NM_EC2_API_VERSION, NM_EC2_METADATA_URL_BASE, ##__VA_ARGS__)
+#define _ec2_uri_concat(...) nmcs_utils_uri_build_concat(_ec2_base(), __VA_ARGS__)
+#define _ec2_uri_interfaces(...) \
+    _ec2_uri_concat(NM_EC2_API_VERSION, NM_EC2_METADATA_URL_BASE, ##__VA_ARGS__)
 
 /*****************************************************************************/
 
 struct _NMCSProviderEC2 {
-	NMCSProvider parent;
+    NMCSProvider parent;
 };
 
 struct _NMCSProviderEC2Class {
-	NMCSProviderClass parent;
+    NMCSProviderClass parent;
 };
 
-G_DEFINE_TYPE (NMCSProviderEC2, nmcs_provider_ec2, NMCS_TYPE_PROVIDER);
+G_DEFINE_TYPE(NMCSProviderEC2, nmcs_provider_ec2, NMCS_TYPE_PROVIDER);
 
 /*****************************************************************************/
 
 static gboolean
-_detect_get_meta_data_check_cb (long response_code,
-                                GBytes *response_data,
-                                gpointer check_user_data,
-                                GError **error)
+_detect_get_meta_data_check_cb(long     response_code,
+                               GBytes * response_data,
+                               gpointer check_user_data,
+                               GError **error)
 {
-	return    response_code == 200
-	       && nmcs_utils_parse_get_full_line (response_data, "ami-id");
+    return response_code == 200 && nmcs_utils_parse_get_full_line(response_data, "ami-id");
 }
 
 static void
-_detect_get_meta_data_done_cb (GObject *source,
-                               GAsyncResult *result,
-                               gpointer user_data)
+_detect_get_meta_data_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
-	gs_unref_object GTask *task = user_data;
-	gs_free_error GError *get_error = NULL;
-	gs_free_error GError *error = NULL;
-	gboolean success;
-
-	success = nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
-	                                          result,
-	                                          NULL,
-	                                          NULL,
-	                                          &get_error);
-
-	if (nm_utils_error_is_cancelled (get_error)) {
-		g_task_return_error (task, g_steal_pointer (&get_error));
-		return;
-	}
-
-	if (get_error) {
-		nm_utils_error_set (&error,
-		                    NM_UTILS_ERROR_UNKNOWN,
-		                    "failure to get EC2 metadata: %s",
-		                    get_error->message);
-		g_task_return_error (task, g_steal_pointer (&error));
-		return;
-	}
-
-	if (!success) {
-		nm_utils_error_set (&error,
-		                    NM_UTILS_ERROR_UNKNOWN,
-		                    "failure to detect EC2 metadata");
-		g_task_return_error (task, g_steal_pointer (&error));
-		return;
-	}
-
-	g_task_return_boolean (task, TRUE);
+    gs_unref_object GTask *task     = user_data;
+    gs_free_error GError *get_error = NULL;
+    gs_free_error GError *error     = NULL;
+
+    nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error);
+
+    if (nm_utils_error_is_cancelled(get_error)) {
+        g_task_return_error(task, g_steal_pointer(&get_error));
+        return;
+    }
+
+    if (get_error) {
+        nm_utils_error_set(&error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           "failure to get EC2 metadata: %s",
+                           get_error->message);
+        g_task_return_error(task, g_steal_pointer(&error));
+        return;
+    }
+
+    g_task_return_boolean(task, TRUE);
 }
 
 static void
-detect (NMCSProvider *provider,
-        GTask *task)
+detect(NMCSProvider *provider, GTask *task)
 {
-	NMHttpClient *http_client;
-	gs_free char *uri = NULL;
-
-	http_client = nmcs_provider_get_http_client (provider);
-
-	nm_http_client_poll_get (http_client,
-	                         (uri = _ec2_uri_concat ("latest/meta-data/")),
-	                         HTTP_TIMEOUT_MS,
-	                         256*1024,
-	                         7000,
-	                         1000,
-	                         NULL,
-	                         g_task_get_cancellable (task),
-	                         _detect_get_meta_data_check_cb,
-	                         NULL,
-	                         _detect_get_meta_data_done_cb,
-	                         task);
+    NMHttpClient *http_client;
+    gs_free char *uri = NULL;
+
+    http_client = nmcs_provider_get_http_client(provider);
+
+    nm_http_client_poll_get(http_client,
+                            (uri = _ec2_uri_concat("latest/meta-data/")),
+                            HTTP_TIMEOUT_MS,
+                            256 * 1024,
+                            7000,
+                            1000,
+                            NULL,
+                            g_task_get_cancellable(task),
+                            _detect_get_meta_data_check_cb,
+                            NULL,
+                            _detect_get_meta_data_done_cb,
+                            task);
 }
 
 /*****************************************************************************/
 
 typedef struct {
-	NMCSProviderGetConfigTaskData *get_config_data;
-	GCancellable *cancellable;
-	gulong cancelled_id;
-	guint n_pending;
+    NMCSProviderGetConfigTaskData *get_config_data;
+    GError *                       error;
+    GCancellable *                 cancellable;
+    gulong                         cancelled_id;
+    guint                          n_pending;
 } GetConfigIfaceData;
 
 static void
-_get_config_task_return (GetConfigIfaceData *iface_data,
-                         GError *error_take)
+_get_config_task_maybe_return(GetConfigIfaceData *iface_data, GError *error_take)
 {
-	NMCSProviderGetConfigTaskData *get_config_data = iface_data->get_config_data;
-
-	nm_clear_g_cancellable_disconnect (g_task_get_cancellable (get_config_data->task),
-	                                   &iface_data->cancelled_id);
-
-	nm_clear_g_cancellable (&iface_data->cancellable);
-
-	nm_g_slice_free (iface_data);
-
-	if (error_take) {
-		if (nm_utils_error_is_cancelled (error_take))
-			_LOGD ("get-config: cancelled");
-		else
-			_LOGD ("get-config: failed: %s", error_take->message);
-		g_task_return_error (get_config_data->task, error_take);
-	} else {
-		_LOGD ("get-config: success");
-		g_task_return_pointer (get_config_data->task,
-		                       g_hash_table_ref (get_config_data->result_dict),
-		                       (GDestroyNotify) g_hash_table_unref);
-	}
-
-	g_object_unref (get_config_data->task);
+    NMCSProviderGetConfigTaskData *get_config_data = iface_data->get_config_data;
+
+    if (error_take) {
+        if (!iface_data->error)
+            iface_data->error = error_take;
+        else if (!nm_utils_error_is_cancelled(iface_data->error)
+                 && nm_utils_error_is_cancelled(error_take)) {
+            nm_clear_error(&iface_data->error);
+            iface_data->error = error_take;
+        } else
+            g_error_free(error_take);
+    }
+
+    if (iface_data->n_pending > 0)
+        return;
+
+    nm_clear_g_cancellable_disconnect(g_task_get_cancellable(get_config_data->task),
+                                      &iface_data->cancelled_id);
+
+    nm_clear_g_cancellable(&iface_data->cancellable);
+
+    if (iface_data->error) {
+        if (nm_utils_error_is_cancelled(iface_data->error))
+            _LOGD("get-config: cancelled");
+        else
+            _LOGD("get-config: failed: %s", iface_data->error->message);
+        g_task_return_error(get_config_data->task, g_steal_pointer(&iface_data->error));
+    } else {
+        _LOGD("get-config: success");
+        g_task_return_pointer(get_config_data->task,
+                              g_hash_table_ref(get_config_data->result_dict),
+                              (GDestroyNotify) g_hash_table_unref);
+    }
+
+    nm_g_slice_free(iface_data);
+    g_object_unref(get_config_data->task);
 }
 
 static void
-_get_config_fetch_done_cb (NMHttpClient *http_client,
-                           GAsyncResult *result,
-                           gpointer user_data,
-                           gboolean is_local_ipv4)
+_get_config_fetch_done_cb(NMHttpClient *http_client,
+                          GAsyncResult *result,
+                          gpointer      user_data,
+                          gboolean      is_local_ipv4)
 {
-	GetConfigIfaceData *iface_data;
-	NMCSProviderGetConfigTaskData *get_config_data;
-	const char *hwaddr = NULL;
-	gs_unref_bytes GBytes *response_data = NULL;
-	gs_free_error GError *error = NULL;
-	gboolean success;
-	NMCSProviderGetConfigIfaceData *config_iface_data;
-
-	nm_utils_user_data_unpack (user_data, &iface_data, &hwaddr);
-
-	success = nm_http_client_poll_get_finish (http_client,
-	                                          result,
-	                                          NULL,
-	                                          &response_data,
-	                                          &error);
-	if (nm_utils_error_is_cancelled (error))
-		return;
-
-	get_config_data = iface_data->get_config_data;
-
-	config_iface_data = g_hash_table_lookup (get_config_data->result_dict, hwaddr);
-
-	if (success) {
-		in_addr_t tmp_addr;
-		int tmp_prefix;
-
-		if (is_local_ipv4) {
-			gs_free const char **s_addrs = NULL;
-			gsize i, len;
-
-			s_addrs = nm_utils_strsplit_set_full (g_bytes_get_data (response_data, NULL), "\n", NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
-			len = NM_PTRARRAY_LEN (s_addrs);
-
-			nm_assert (!config_iface_data->has_ipv4s);
-			nm_assert (!config_iface_data->ipv4s_arr);
-			config_iface_data->has_ipv4s = TRUE;
-			config_iface_data->ipv4s_len = 0;
-			if (len > 0) {
-				config_iface_data->ipv4s_arr = g_new (in_addr_t, len);
-
-				for (i = 0; i < len; i++) {
-					if (nm_utils_parse_inaddr_bin (AF_INET,
-					                               s_addrs[i],
-					                               NULL,
-					                               &tmp_addr))
-						config_iface_data->ipv4s_arr[config_iface_data->ipv4s_len++] = tmp_addr;
-				}
-			}
-		} else {
-			if (nm_utils_parse_inaddr_prefix_bin (AF_INET,
-			                                      g_bytes_get_data (response_data, NULL),
-			                                      NULL,
-			                                      &tmp_addr,
-			                                      &tmp_prefix)) {
-				nm_assert (!config_iface_data->has_cidr);
-				config_iface_data->has_cidr = TRUE;
-				config_iface_data->cidr_prefix = tmp_prefix;
-				config_iface_data->cidr_addr = tmp_addr;
-			}
-		}
-	}
-
-	if (--iface_data->n_pending > 0)
-		return;
-
-	_get_config_task_return (iface_data, NULL);
+    GetConfigIfaceData *iface_data;
+    const char *        hwaddr           = NULL;
+    gs_unref_bytes GBytes *response_data = NULL;
+    gs_free_error GError *error          = NULL;
+
+    nm_utils_user_data_unpack(user_data, &iface_data, &hwaddr);
+
+    nm_http_client_poll_get_finish(http_client, result, NULL, &response_data, &error);
+
+    if (!error) {
+        NMCSProviderGetConfigIfaceData *config_iface_data;
+        in_addr_t                       tmp_addr;
+        int                             tmp_prefix;
+
+        config_iface_data = g_hash_table_lookup(iface_data->get_config_data->result_dict, hwaddr);
+
+        if (is_local_ipv4) {
+            gs_free const char **s_addrs = NULL;
+            gsize                i, len;
+
+            s_addrs = nm_utils_strsplit_set_full(g_bytes_get_data(response_data, NULL),
+                                                 "\n",
+                                                 NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
+            len     = NM_PTRARRAY_LEN(s_addrs);
+
+            nm_assert(!config_iface_data->has_ipv4s);
+            nm_assert(!config_iface_data->ipv4s_arr);
+            config_iface_data->has_ipv4s = TRUE;
+            config_iface_data->ipv4s_len = 0;
+            if (len > 0) {
+                config_iface_data->ipv4s_arr = g_new(in_addr_t, len);
+
+                for (i = 0; i < len; i++) {
+                    if (nm_utils_parse_inaddr_bin(AF_INET, s_addrs[i], NULL, &tmp_addr))
+                        config_iface_data->ipv4s_arr[config_iface_data->ipv4s_len++] = tmp_addr;
+                }
+            }
+        } else {
+            if (nm_utils_parse_inaddr_prefix_bin(AF_INET,
+                                                 g_bytes_get_data(response_data, NULL),
+                                                 NULL,
+                                                 &tmp_addr,
+                                                 &tmp_prefix)) {
+                nm_assert(!config_iface_data->has_cidr);
+                config_iface_data->has_cidr    = TRUE;
+                config_iface_data->cidr_prefix = tmp_prefix;
+                config_iface_data->cidr_addr   = tmp_addr;
+            }
+        }
+    }
+
+    iface_data->n_pending--;
+    _get_config_task_maybe_return(iface_data, g_steal_pointer(&error));
 }
 
 static void
-_get_config_fetch_done_cb_subnet_ipv4_cidr_block (GObject *source,
-                                                  GAsyncResult *result,
-                                                  gpointer user_data)
+_get_config_fetch_done_cb_subnet_ipv4_cidr_block(GObject *     source,
+                                                 GAsyncResult *result,
+                                                 gpointer      user_data)
 {
-	_get_config_fetch_done_cb (NM_HTTP_CLIENT (source), result, user_data, FALSE);
+    _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), result, user_data, FALSE);
 }
 
 static void
-_get_config_fetch_done_cb_local_ipv4s (GObject *source,
-                                       GAsyncResult *result,
-                                       gpointer user_data)
+_get_config_fetch_done_cb_local_ipv4s(GObject *source, GAsyncResult *result, gpointer user_data)
 {
-	_get_config_fetch_done_cb (NM_HTTP_CLIENT (source), result, user_data, TRUE);
+    _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), result, user_data, TRUE);
 }
 
 static void
-_get_config_fetch_cancelled_cb (GObject *object, gpointer user_data)
+_get_config_fetch_cancelled_cb(GObject *object, gpointer user_data)
 {
-	GetConfigIfaceData *iface_data = user_data;
+    GetConfigIfaceData *iface_data = user_data;
 
-	if (iface_data->cancelled_id == 0)
-		return;
+    if (iface_data->cancelled_id == 0)
+        return;
 
-	nm_clear_g_signal_handler (g_task_get_cancellable (iface_data->get_config_data->task),
-	                           &iface_data->cancelled_id);
-	_get_config_task_return (iface_data,
-	                         nm_utils_error_new_cancelled (FALSE, NULL));
+    nm_clear_g_signal_handler(g_task_get_cancellable(iface_data->get_config_data->task),
+                              &iface_data->cancelled_id);
+    _get_config_task_maybe_return(iface_data, nm_utils_error_new_cancelled(FALSE, NULL));
 }
 
 typedef struct {
-	NMCSProviderGetConfigTaskData *get_config_data;
-	GHashTable *response_parsed;
+    NMCSProviderGetConfigTaskData *get_config_data;
+    GHashTable *                   response_parsed;
 } GetConfigMetadataData;
 
 typedef struct {
-	gssize iface_idx;
-	char path[0];
+    gssize iface_idx;
+    char   path[0];
 } GetConfigMetadataMac;
 
 static void
-_get_config_metadata_ready_cb (GObject *source,
-                               GAsyncResult *result,
-                               gpointer user_data)
+_get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
-	GetConfigMetadataData *metadata_data = user_data;
-	GetConfigIfaceData *iface_data;
-	NMCSProviderGetConfigTaskData *get_config_data = metadata_data->get_config_data;
-	gs_unref_hashtable GHashTable *response_parsed = g_steal_pointer (&metadata_data->response_parsed);
-	gs_free_error GError *error = NULL;
-	GCancellable *cancellable;
-	GetConfigMetadataMac *v_mac_data;
-	const char *v_hwaddr;
-	GHashTableIter h_iter;
-	NMHttpClient *http_client;
-
-	nm_g_slice_free (metadata_data);
-
-	nm_http_client_poll_get_finish (NM_HTTP_CLIENT (source),
-	                                result,
-	                                NULL,
-	                                NULL,
-	                                &error);
-
-	iface_data = g_slice_new (GetConfigIfaceData);
-	*iface_data = (GetConfigIfaceData) {
-		.get_config_data = get_config_data,
-		.n_pending       = 0,
-	};
-
-	if (nm_utils_error_is_cancelled (error)) {
-		_get_config_task_return (iface_data, g_steal_pointer (&error));
-		return;
-	}
-
-	/* We ignore errors. Only if we got no response at all, it's a problem.
-	 * Otherwise, we proceed with whatever we could fetch. */
-	if (!response_parsed) {
-		_get_config_task_return (iface_data,
-		                         nm_utils_error_new (NM_UTILS_ERROR_UNKNOWN,
-		                                             "meta data for interfaces not found"));
-		return;
-	}
-
-	cancellable = g_task_get_cancellable (get_config_data->task);
-	if (cancellable) {
-		gulong cancelled_id;
-
-		cancelled_id = g_cancellable_connect (cancellable,
-		                                      G_CALLBACK (_get_config_fetch_cancelled_cb),
-		                                      iface_data,
-		                                      NULL);
-		if (cancelled_id == 0) {
-			_get_config_task_return (iface_data,
-			                         nm_utils_error_new_cancelled (FALSE, NULL));
-			return;
-		}
-
-		iface_data->cancelled_id = cancelled_id;
-	}
-
-	iface_data->cancellable = g_cancellable_new ();
-
-	http_client = nmcs_provider_get_http_client (g_task_get_source_object (get_config_data->task));
-
-	g_hash_table_iter_init (&h_iter, response_parsed);
-	while (g_hash_table_iter_next (&h_iter, (gpointer *) &v_hwaddr, (gpointer *) &v_mac_data)) {
-		NMCSProviderGetConfigIfaceData *config_iface_data;
-		gs_free char *uri1 = NULL;
-		gs_free char *uri2 = NULL;
-		const char *hwaddr;
-
-		if (!g_hash_table_lookup_extended (get_config_data->result_dict, v_hwaddr, (gpointer *) &hwaddr, (gpointer *) &config_iface_data)) {
-			if (!get_config_data->any) {
-				_LOGD ("get-config: skip fetching meta data for %s (%s)", v_hwaddr, v_mac_data->path);
-				continue;
-			}
-			config_iface_data = nmcs_provider_get_config_iface_data_new (FALSE);
-			g_hash_table_insert (get_config_data->result_dict,
-			                     (char *) (hwaddr = g_strdup (v_hwaddr)),
-			                     config_iface_data);
-		}
-
-		nm_assert (config_iface_data->iface_idx == -1);
-		config_iface_data->iface_idx = v_mac_data->iface_idx;
-
-		_LOGD ("get-config: start fetching meta data for #%"G_GSSIZE_FORMAT", %s (%s)", config_iface_data->iface_idx, hwaddr, v_mac_data->path);
-
-		iface_data->n_pending++;
-		nm_http_client_poll_get (http_client,
-		                         (uri1 = _ec2_uri_interfaces (v_mac_data->path,
-		                                                        NM_STR_HAS_SUFFIX (v_mac_data->path, "/")
-		                                                      ? ""
-		                                                      : "/",
-		                                                      "subnet-ipv4-cidr-block")),
-		                         HTTP_TIMEOUT_MS,
-		                         512*1024,
-		                         10000,
-		                         1000,
-		                         NULL,
-		                         iface_data->cancellable,
-		                         NULL,
-		                         NULL,
-		                         _get_config_fetch_done_cb_subnet_ipv4_cidr_block,
-		                         nm_utils_user_data_pack (iface_data, hwaddr));
-
-		iface_data->n_pending++;
-		nm_http_client_poll_get (http_client,
-		                         (uri2 = _ec2_uri_interfaces (v_mac_data->path,
-		                                                        NM_STR_HAS_SUFFIX (v_mac_data->path, "/")
-		                                                      ? ""
-		                                                      : "/",
-		                                                      "local-ipv4s")),
-		                         HTTP_TIMEOUT_MS,
-		                         512*1024,
-		                         10000,
-		                         1000,
-		                         NULL,
-		                         iface_data->cancellable,
-		                         NULL,
-		                         NULL,
-		                         _get_config_fetch_done_cb_local_ipv4s,
-		                         nm_utils_user_data_pack (iface_data, hwaddr));
-	}
-
-	if (iface_data->n_pending == 0)
-		_get_config_task_return (iface_data, NULL);
+    GetConfigMetadataData *        metadata_data = user_data;
+    GetConfigIfaceData *           iface_data;
+    NMCSProviderGetConfigTaskData *get_config_data = metadata_data->get_config_data;
+    gs_unref_hashtable GHashTable *response_parsed =
+        g_steal_pointer(&metadata_data->response_parsed);
+    gs_free_error GError *error = NULL;
+    GCancellable *        cancellable;
+    GetConfigMetadataMac *v_mac_data;
+    const char *          v_hwaddr;
+    GHashTableIter        h_iter;
+    NMHttpClient *        http_client;
+
+    nm_g_slice_free(metadata_data);
+
+    nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &error);
+
+    iface_data  = g_slice_new(GetConfigIfaceData);
+    *iface_data = (GetConfigIfaceData){
+        .get_config_data = get_config_data,
+        .n_pending       = 0,
+    };
+
+    if (nm_utils_error_is_cancelled(error)) {
+        _get_config_task_maybe_return(iface_data, g_steal_pointer(&error));
+        return;
+    }
+
+    /* We ignore errors. Only if we got no response at all, it's a problem.
+     * Otherwise, we proceed with whatever we could fetch. */
+    if (!response_parsed) {
+        _get_config_task_maybe_return(
+            iface_data,
+            nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "meta data for interfaces not found"));
+        return;
+    }
+
+    cancellable = g_task_get_cancellable(get_config_data->task);
+    if (cancellable) {
+        gulong cancelled_id;
+
+        cancelled_id = g_cancellable_connect(cancellable,
+                                             G_CALLBACK(_get_config_fetch_cancelled_cb),
+                                             iface_data,
+                                             NULL);
+        if (cancelled_id == 0) {
+            _get_config_task_maybe_return(iface_data, nm_utils_error_new_cancelled(FALSE, NULL));
+            return;
+        }
+
+        iface_data->cancelled_id = cancelled_id;
+    }
+
+    iface_data->cancellable = g_cancellable_new();
+
+    http_client = nmcs_provider_get_http_client(g_task_get_source_object(get_config_data->task));
+
+    g_hash_table_iter_init(&h_iter, response_parsed);
+    while (g_hash_table_iter_next(&h_iter, (gpointer *) &v_hwaddr, (gpointer *) &v_mac_data)) {
+        NMCSProviderGetConfigIfaceData *config_iface_data;
+        gs_free char *                  uri1 = NULL;
+        gs_free char *                  uri2 = NULL;
+        const char *                    hwaddr;
+
+        if (!g_hash_table_lookup_extended(get_config_data->result_dict,
+                                          v_hwaddr,
+                                          (gpointer *) &hwaddr,
+                                          (gpointer *) &config_iface_data)) {
+            if (!get_config_data->any) {
+                _LOGD("get-config: skip fetching meta data for %s (%s)",
+                      v_hwaddr,
+                      v_mac_data->path);
+                continue;
+            }
+            config_iface_data = nmcs_provider_get_config_iface_data_new(FALSE);
+            g_hash_table_insert(get_config_data->result_dict,
+                                (char *) (hwaddr = g_strdup(v_hwaddr)),
+                                config_iface_data);
+        }
+
+        nm_assert(config_iface_data->iface_idx == -1);
+        config_iface_data->iface_idx = v_mac_data->iface_idx;
+
+        _LOGD("get-config: start fetching meta data for #%" G_GSSIZE_FORMAT ", %s (%s)",
+              config_iface_data->iface_idx,
+              hwaddr,
+              v_mac_data->path);
+
+        iface_data->n_pending++;
+        nm_http_client_poll_get(
+            http_client,
+            (uri1 = _ec2_uri_interfaces(v_mac_data->path,
+                                        NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/",
+                                        "subnet-ipv4-cidr-block")),
+            HTTP_TIMEOUT_MS,
+            512 * 1024,
+            10000,
+            1000,
+            NULL,
+            iface_data->cancellable,
+            NULL,
+            NULL,
+            _get_config_fetch_done_cb_subnet_ipv4_cidr_block,
+            nm_utils_user_data_pack(iface_data, hwaddr));
+
+        iface_data->n_pending++;
+        nm_http_client_poll_get(
+            http_client,
+            (uri2 = _ec2_uri_interfaces(v_mac_data->path,
+                                        NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/",
+                                        "local-ipv4s")),
+            HTTP_TIMEOUT_MS,
+            512 * 1024,
+            10000,
+            1000,
+            NULL,
+            iface_data->cancellable,
+            NULL,
+            NULL,
+            _get_config_fetch_done_cb_local_ipv4s,
+            nm_utils_user_data_pack(iface_data, hwaddr));
+    }
+
+    _get_config_task_maybe_return(iface_data, NULL);
 }
 
 static gboolean
-_get_config_metadata_ready_check (long response_code,
-                                  GBytes *response_data,
-                                  gpointer check_user_data,
-                                  GError **error)
+_get_config_metadata_ready_check(long     response_code,
+                                 GBytes * response_data,
+                                 gpointer check_user_data,
+                                 GError **error)
 {
-	GetConfigMetadataData *metadata_data = check_user_data;
-	gs_unref_hashtable GHashTable *response_parsed = NULL;
-	const guint8 *r_data;
-	gsize r_len;
-	GHashTableIter h_iter;
-	gboolean has_all;
-	const char *c_hwaddr;
-	gssize iface_idx_counter = 0;
-
-	if (   response_code != 200
-	    || !response_data) {
-		/* we wait longer. */
-		return FALSE;
-	}
-
-	r_data = g_bytes_get_data (response_data, &r_len);
-
-	while (r_len > 0) {
-		const guint8 *p_eol;
-		const char *p_start;
-		gsize p_start_l;
-		gsize p_start_l_2;
-		char *hwaddr;
-		GetConfigMetadataMac *mac_data;
-
-		p_start = (const char *) r_data;
-
-		p_eol = memchr (r_data, '\n', r_len);
-		if (p_eol) {
-			p_start_l = (p_eol - r_data);
-			r_len -= p_start_l + 1;
-			r_data = &p_eol[1];
-		} else {
-			p_start_l = r_len;
-			r_data = &r_data[r_len];
-			r_len = 0;
-		}
-
-		if (p_start_l == 0)
-			continue;
-
-		p_start_l_2 = p_start_l;
-		if (p_start[p_start_l_2 - 1] == '/') {
-			/* trim the trailing "/". */
-			p_start_l_2--;
-		}
-
-		hwaddr = nmcs_utils_hwaddr_normalize (p_start, p_start_l_2);
-		if (!hwaddr)
-			continue;
-
-		if (!response_parsed)
-			response_parsed = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
-
-		mac_data = g_malloc (sizeof (GetConfigMetadataMac) + 1 + p_start_l);
-		mac_data->iface_idx = iface_idx_counter++;
-		memcpy (mac_data->path, p_start, p_start_l);
-		mac_data->path[p_start_l] = '\0';
-
-		g_hash_table_insert (response_parsed, hwaddr, mac_data);
-	}
-
-	has_all = TRUE;
-	g_hash_table_iter_init (&h_iter, metadata_data->get_config_data->result_dict);
-	while (g_hash_table_iter_next (&h_iter, (gpointer *) &c_hwaddr, NULL)) {
-		if (   !response_parsed
-		    || !g_hash_table_contains (response_parsed, c_hwaddr)) {
-			has_all = FALSE;
-			break;
-		}
-	}
-
-	nm_clear_pointer (&metadata_data->response_parsed, g_hash_table_unref);
-	metadata_data->response_parsed = g_steal_pointer (&response_parsed);
-	return has_all;
+    GetConfigMetadataData *metadata_data           = check_user_data;
+    gs_unref_hashtable GHashTable *response_parsed = NULL;
+    const guint8 *                 r_data;
+    const char *                   cur_line;
+    gsize                          r_len;
+    gsize                          cur_line_len;
+    GHashTableIter                 h_iter;
+    gboolean                       has_all;
+    const char *                   c_hwaddr;
+    gssize                         iface_idx_counter = 0;
+
+    if (response_code != 200 || !response_data) {
+        /* we wait longer. */
+        return FALSE;
+    }
+
+    r_data = g_bytes_get_data(response_data, &r_len);
+    /* NMHttpClient guarantees that there is a trailing NUL after the data. */
+    nm_assert(r_data[r_len] == 0);
+
+    while (nm_utils_parse_next_line((const char **) &r_data, &r_len, &cur_line, &cur_line_len)) {
+        GetConfigMetadataMac *mac_data;
+        char *                hwaddr;
+
+        if (cur_line_len == 0)
+            continue;
+
+        /* Truncate the string. It's safe to do, because we own @response_data an it has an
+         * extra NUL character after the buffer. */
+        ((char *) cur_line)[cur_line_len] = '\0';
+
+        hwaddr = nmcs_utils_hwaddr_normalize(
+            cur_line,
+            cur_line[cur_line_len - 1u] == '/' ? (gssize)(cur_line_len - 1u) : -1);
+        if (!hwaddr)
+            continue;
+
+        if (!response_parsed)
+            response_parsed = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
+
+        mac_data            = g_malloc(sizeof(GetConfigMetadataMac) + 1u + cur_line_len);
+        mac_data->iface_idx = iface_idx_counter++;
+        memcpy(mac_data->path, cur_line, cur_line_len + 1u);
+
+        g_hash_table_insert(response_parsed, hwaddr, mac_data);
+    }
+
+    has_all = TRUE;
+    g_hash_table_iter_init(&h_iter, metadata_data->get_config_data->result_dict);
+    while (g_hash_table_iter_next(&h_iter, (gpointer *) &c_hwaddr, NULL)) {
+        if (!response_parsed || !g_hash_table_contains(response_parsed, c_hwaddr)) {
+            has_all = FALSE;
+            break;
+        }
+    }
+
+    nm_clear_pointer(&metadata_data->response_parsed, g_hash_table_unref);
+    metadata_data->response_parsed = g_steal_pointer(&response_parsed);
+    return has_all;
 }
 
 static void
-get_config (NMCSProvider *provider,
-            NMCSProviderGetConfigTaskData *get_config_data)
+get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data)
 {
-	gs_free char *uri = NULL;
-	GetConfigMetadataData *metadata_data;
-
-	metadata_data = g_slice_new (GetConfigMetadataData);
-	*metadata_data = (GetConfigMetadataData) {
-		.get_config_data = get_config_data,
-	};
-
-	/* First we fetch the "macs/". If the caller requested some particular
-	 * MAC addresses, then we poll until we see them. They might not yet be
-	 * around from the start...
-	 */
-	nm_http_client_poll_get (nmcs_provider_get_http_client (provider),
-	                         (uri = _ec2_uri_interfaces ()),
-	                         HTTP_TIMEOUT_MS,
-	                         256 * 1024,
-	                         15000,
-	                         1000,
-	                         NULL,
-	                         g_task_get_cancellable (get_config_data->task),
-	                         _get_config_metadata_ready_check,
-	                         metadata_data,
-	                         _get_config_metadata_ready_cb,
-	                         metadata_data);
+    gs_free char *         uri = NULL;
+    GetConfigMetadataData *metadata_data;
+
+    metadata_data  = g_slice_new(GetConfigMetadataData);
+    *metadata_data = (GetConfigMetadataData){
+        .get_config_data = get_config_data,
+    };
+
+    /* First we fetch the "macs/". If the caller requested some particular
+     * MAC addresses, then we poll until we see them. They might not yet be
+     * around from the start...
+     */
+    nm_http_client_poll_get(nmcs_provider_get_http_client(provider),
+                            (uri = _ec2_uri_interfaces()),
+                            HTTP_TIMEOUT_MS,
+                            256 * 1024,
+                            15000,
+                            1000,
+                            NULL,
+                            g_task_get_cancellable(get_config_data->task),
+                            _get_config_metadata_ready_check,
+                            metadata_data,
+                            _get_config_metadata_ready_cb,
+                            metadata_data);
 }
 
 /*****************************************************************************/
 
 static void
-nmcs_provider_ec2_init (NMCSProviderEC2 *self)
-{
-}
+nmcs_provider_ec2_init(NMCSProviderEC2 *self)
+{}
 
 static void
-nmcs_provider_ec2_class_init (NMCSProviderEC2Class *klass)
+nmcs_provider_ec2_class_init(NMCSProviderEC2Class *klass)
 {
-	NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS (klass);
+    NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
 
-	provider_class->_name                 = "ec2";
-	provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE ("NM_CLOUD_SETUP_EC2");
-	provider_class->detect                = detect;
-	provider_class->get_config            = get_config;
+    provider_class->_name                 = "ec2";
+    provider_class->_env_provider_enabled = NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_EC2");
+    provider_class->detect                = detect;
+    provider_class->get_config            = get_config;
 }