about summary refs log tree commit diff
path: root/clients/cloud-setup/nmcs-provider-azure.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/cloud-setup/nmcs-provider-azure.c')
-rw-r--r--clients/cloud-setup/nmcs-provider-azure.c342
1 files changed, 169 insertions, 173 deletions
diff --git a/clients/cloud-setup/nmcs-provider-azure.c b/clients/cloud-setup/nmcs-provider-azure.c
index 1a80327c..0a5d522c 100644
--- a/clients/cloud-setup/nmcs-provider-azure.c
+++ b/clients/cloud-setup/nmcs-provider-azure.c
@@ -1,6 +1,6 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include "nm-default.h"
+#include "libnm/nm-default-client.h"
 
 #include "nmcs-provider-azure.h"
 
@@ -94,128 +94,97 @@ detect(NMCSProvider *provider, GTask *task)
 /*****************************************************************************/
 
 typedef struct {
-    NMCSProviderGetConfigTaskData *config_data;
-    guint                          n_ifaces_pending;
-    GError *                       error;
-} AzureData;
-
-typedef struct {
+    NMCSProviderGetConfigTaskData * get_config_data;
     NMCSProviderGetConfigIfaceData *iface_get_config;
-    AzureData *                     azure_data;
-    gssize                          iface_idx;
+    gssize                          intern_iface_idx;
+    gssize                          extern_iface_idx;
     guint                           n_ips_prefix_pending;
-    char *                          hwaddr;
+    const char *                    hwaddr;
 } AzureIfaceData;
 
 static void
-_azure_iface_data_free(AzureIfaceData *iface_data)
+_azure_iface_data_destroy(AzureIfaceData *iface_data)
 {
-    g_free(iface_data->hwaddr);
     nm_g_slice_free(iface_data);
 }
 
 static void
-_get_config_maybe_task_return(AzureData *azure_data, GError *error_take)
+_get_config_fetch_done_cb(NMHttpClient *  http_client,
+                          GAsyncResult *  result,
+                          AzureIfaceData *iface_data,
+                          gboolean        is_ipv4)
 {
-    NMCSProviderGetConfigTaskData *config_data = azure_data->config_data;
-
-    if (error_take) {
-        if (!azure_data->error)
-            azure_data->error = error_take;
-        else if (!nm_utils_error_is_cancelled(azure_data->error)
-                 && nm_utils_error_is_cancelled(error_take)) {
-            nm_clear_error(&azure_data->error);
-            azure_data->error = error_take;
-        } else
-            g_error_free(error_take);
-    }
+    NMCSProviderGetConfigTaskData * get_config_data;
+    NMCSProviderGetConfigIfaceData *iface_get_config;
+    gs_unref_bytes GBytes *response = NULL;
+    gs_free_error GError *error     = NULL;
+    const char *          fip_str   = NULL;
+    gsize                 fip_len;
+
+    nm_http_client_poll_get_finish(http_client, result, NULL, &response, &error);
 
-    if (azure_data->n_ifaces_pending > 0)
+    if (nm_utils_error_is_cancelled(error))
         return;
 
-    if (azure_data->error) {
-        if (nm_utils_error_is_cancelled(azure_data->error))
-            _LOGD("get-config: cancelled");
-        else
-            _LOGD("get-config: failed: %s", azure_data->error->message);
-        g_task_return_error(config_data->task, g_steal_pointer(&azure_data->error));
-    } else {
-        _LOGD("get-config: success");
-        g_task_return_pointer(config_data->task,
-                              g_hash_table_ref(config_data->result_dict),
-                              (GDestroyNotify) g_hash_table_unref);
-    }
+    get_config_data = iface_data->get_config_data;
 
-    nm_g_slice_free(azure_data);
-    g_object_unref(config_data->task);
-}
+    if (error)
+        goto out_done;
 
-static void
-_get_config_fetch_done_cb(NMHttpClient *http_client,
-                          GAsyncResult *result,
-                          gpointer      user_data,
-                          gboolean      is_ipv4)
-{
-    NMCSProviderGetConfigIfaceData *iface_get_config;
-    gs_unref_bytes GBytes *response   = NULL;
-    AzureIfaceData *       iface_data = user_data;
-    gs_free_error GError *error       = NULL;
-    const char *          fip_str     = NULL;
-    AzureData *           azure_data;
+    fip_str = g_bytes_get_data(response, &fip_len);
+    nm_assert(fip_str[fip_len] == '\0');
 
-    azure_data = iface_data->azure_data;
+    iface_data->iface_get_config =
+        g_hash_table_lookup(get_config_data->result_dict, iface_data->hwaddr);
+    iface_get_config = iface_data->iface_get_config;
 
-    nm_http_client_poll_get_finish(http_client, result, NULL, &response, &error);
+    if (is_ipv4) {
+        char      tmp_addr_str[NM_UTILS_INET_ADDRSTRLEN];
+        in_addr_t tmp_addr;
 
-    if (error)
-        goto done;
+        if (!nmcs_utils_ipaddr_normalize_bin(AF_INET, fip_str, fip_len, NULL, &tmp_addr)) {
+            error =
+                nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "ip is not a valid private ip address");
+            goto out_done;
+        }
+        _LOGD("interface[%" G_GSSIZE_FORMAT "]: adding private ip %s",
+              iface_data->intern_iface_idx,
+              _nm_utils_inet4_ntop(tmp_addr, tmp_addr_str));
+        iface_get_config->ipv4s_arr[iface_get_config->ipv4s_len] = tmp_addr;
+        iface_get_config->has_ipv4s                              = TRUE;
+        iface_get_config->ipv4s_len++;
+    } else {
+        int tmp_prefix = -1;
 
-    if (!error) {
-        in_addr_t tmp_addr;
-        int       tmp_prefix;
-
-        fip_str = g_bytes_get_data(response, NULL);
-        iface_data->iface_get_config =
-            g_hash_table_lookup(azure_data->config_data->result_dict, iface_data->hwaddr);
-        iface_get_config            = iface_data->iface_get_config;
-        iface_get_config->iface_idx = iface_data->iface_idx;
-
-        if (is_ipv4) {
-            if (!nm_utils_parse_inaddr_bin(AF_INET, fip_str, NULL, &tmp_addr)) {
-                error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN,
-                                           "ip is not a valid private ip address");
-                goto done;
-            }
-            _LOGD("interface[%" G_GSSIZE_FORMAT "]: adding private ip %s",
-                  iface_data->iface_idx,
-                  fip_str);
-            iface_get_config->ipv4s_arr[iface_get_config->ipv4s_len] = tmp_addr;
-            iface_get_config->has_ipv4s                              = TRUE;
-            iface_get_config->ipv4s_len++;
-        } else {
-            tmp_prefix = (_nm_utils_ascii_str_to_int64(fip_str, 10, 0, 32, -1));
-
-            if (tmp_prefix == -1) {
-                _LOGD("interface[%" G_GSSIZE_FORMAT "]: invalid prefix %d",
-                      iface_data->iface_idx,
-                      tmp_prefix);
-                goto done;
-            }
-            _LOGD("interface[%" G_GSSIZE_FORMAT "]: adding prefix %d",
-                  iface_data->iface_idx,
-                  tmp_prefix);
-            iface_get_config->cidr_prefix = tmp_prefix;
-            iface_get_config->has_cidr    = TRUE;
+        if (fip_len > 0 && memchr(fip_str, '\0', fip_len - 1)) {
+            /* we have an embedded "\0" inside the string (except trailing). That is not
+             * allowed*/
+        } else
+            tmp_prefix = _nm_utils_ascii_str_to_int64(fip_str, 10, 0, 32, -1);
+
+        if (tmp_prefix == -1) {
+            _LOGD("interface[%" G_GSSIZE_FORMAT "]: invalid prefix", iface_data->intern_iface_idx);
+            error =
+                nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "subnet does not give a valid prefix");
+            goto out_done;
         }
+
+        _LOGD("interface[%" G_GSSIZE_FORMAT "]: adding prefix %d",
+              iface_data->intern_iface_idx,
+              tmp_prefix);
+        iface_get_config->cidr_prefix = tmp_prefix;
+        iface_get_config->has_cidr    = TRUE;
     }
 
-done:
-    --iface_data->n_ips_prefix_pending;
-    if (iface_data->n_ips_prefix_pending == 0) {
-        _azure_iface_data_free(iface_data);
-        --azure_data->n_ifaces_pending;
-        _get_config_maybe_task_return(azure_data, g_steal_pointer(&error));
+out_done:
+    if (!error) {
+        --iface_data->n_ips_prefix_pending;
+        if (iface_data->n_ips_prefix_pending > 0)
+            return;
     }
+
+    --get_config_data->n_pending;
+    _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
 }
 
 static void
@@ -235,20 +204,24 @@ _get_config_fetch_done_cb_subnet_cidr_prefix(GObject *     source,
 static void
 _get_config_ips_prefix_list_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
-    gs_unref_bytes GBytes *response    = NULL;
-    AzureIfaceData *       iface_data  = user_data;
-    gs_free_error GError *error        = NULL;
-    const char *          response_str = NULL;
-    gsize                 response_len;
-    AzureData *           azure_data;
-    const char *          line;
-    gsize                 line_len;
-
-    azure_data = iface_data->azure_data;
+    gs_unref_bytes GBytes *response             = NULL;
+    AzureIfaceData *       iface_data           = user_data;
+    gs_free_error GError *         error        = NULL;
+    const char *                   response_str = NULL;
+    gsize                          response_len;
+    NMCSProviderGetConfigTaskData *get_config_data;
+    const char *                   line;
+    gsize                          line_len;
 
     nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error);
+
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    get_config_data = iface_data->get_config_data;
+
     if (error)
-        goto done;
+        goto out_error;
 
     response_str = g_bytes_get_data(response, &response_len);
     /* NMHttpClient guarantees that there is a trailing NUL after the data. */
@@ -263,7 +236,7 @@ _get_config_ips_prefix_list_cb(GObject *source, GAsyncResult *result, gpointer u
 
         if (line_len == 0)
             continue;
-        /* Truncate the string. It's safe to do, because we own @response_data an it has an
+        /* Truncate the string. It's safe to do, because we own @response an it has an
          * extra NULL character after the buffer. */
         ((char *) line)[line_len] = '\0';
 
@@ -286,14 +259,14 @@ _get_config_ips_prefix_list_cb(GObject *source, GAsyncResult *result, gpointer u
                 (uri = _azure_uri_interfaces(nm_sprintf_buf(
                      buf,
                      "%" G_GSSIZE_FORMAT "/ipv4/ipAddress/%" G_GINT64_FORMAT "/privateIpAddress",
-                     iface_data->iface_idx,
+                     iface_data->intern_iface_idx,
                      ips_prefix_idx))),
                 HTTP_TIMEOUT_MS,
                 512 * 1024,
                 10000,
                 1000,
                 NM_MAKE_STRV(NM_AZURE_METADATA_HEADER),
-                g_task_get_cancellable(azure_data->config_data->task),
+                get_config_data->intern_cancellable,
                 NULL,
                 NULL,
                 _get_config_fetch_done_cb_private_ipv4s,
@@ -312,14 +285,14 @@ _get_config_ips_prefix_list_cb(GObject *source, GAsyncResult *result, gpointer u
         nm_http_client_poll_get(
             NM_HTTP_CLIENT(source),
             (uri = _azure_uri_interfaces(
-                 nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT, iface_data->iface_idx),
+                 nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT, iface_data->intern_iface_idx),
                  "/ipv4/subnet/0/prefix/")),
             HTTP_TIMEOUT_MS,
             512 * 1024,
             10000,
             1000,
             NM_MAKE_STRV(NM_AZURE_METADATA_HEADER),
-            g_task_get_cancellable(azure_data->config_data->task),
+            get_config_data->intern_cancellable,
             NULL,
             NULL,
             _get_config_fetch_done_cb_subnet_cidr_prefix,
@@ -327,56 +300,75 @@ _get_config_ips_prefix_list_cb(GObject *source, GAsyncResult *result, gpointer u
     }
     return;
 
-done:
-    _azure_iface_data_free(iface_data);
-    --azure_data->n_ifaces_pending;
-    _get_config_maybe_task_return(azure_data, g_steal_pointer(&error));
+out_error:
+    --get_config_data->n_pending;
+    _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
 }
 
 static void
 _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
+    NMCSProviderGetConfigTaskData *get_config_data;
     gs_unref_bytes GBytes *response   = NULL;
     AzureIfaceData *       iface_data = user_data;
+    gs_free char *         v_hwaddr   = NULL;
     gs_free_error GError *error       = NULL;
     gs_free const char *  uri         = NULL;
     char                  buf[100];
-    AzureData *           azure_data;
-
-    azure_data = iface_data->azure_data;
 
     nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error);
 
-    if (error)
-        goto done;
+    if (nm_utils_error_is_cancelled(error))
+        return;
 
-    iface_data->hwaddr = nmcs_utils_hwaddr_normalize(g_bytes_get_data(response, NULL), -1);
+    get_config_data = iface_data->get_config_data;
 
-    if (!iface_data->hwaddr) {
-        goto done;
+    if (error)
+        goto out_done;
+
+    v_hwaddr = nmcs_utils_hwaddr_normalize_gbytes(response);
+    if (!v_hwaddr) {
+        _LOGI("interface[%" G_GSSIZE_FORMAT "]: invalid MAC address returned",
+              iface_data->intern_iface_idx);
+        error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN,
+                                   "invalid MAC address for index %" G_GSSIZE_FORMAT,
+                                   iface_data->intern_iface_idx);
+        goto out_done;
     }
 
-    iface_data->iface_get_config =
-        g_hash_table_lookup(azure_data->config_data->result_dict, iface_data->hwaddr);
-
-    if (!iface_data->iface_get_config) {
-        if (!iface_data->azure_data->config_data->any) {
-            _LOGD("interface[%" G_GSSIZE_FORMAT "]: ignore hwaddr %s",
-                  iface_data->iface_idx,
-                  iface_data->hwaddr);
-            goto done;
+    if (!g_hash_table_lookup_extended(get_config_data->result_dict,
+                                      v_hwaddr,
+                                      (gpointer *) &iface_data->hwaddr,
+                                      (gpointer *) &iface_data->iface_get_config)) {
+        if (!get_config_data->any) {
+            _LOGD("get-config: skip fetching meta data for %s (%" G_GSSIZE_FORMAT ")",
+                  v_hwaddr,
+                  iface_data->intern_iface_idx);
+            goto out_done;
         }
         iface_data->iface_get_config = nmcs_provider_get_config_iface_data_new(FALSE);
-        g_hash_table_insert(azure_data->config_data->result_dict,
-                            g_strdup(iface_data->hwaddr),
+        g_hash_table_insert(get_config_data->result_dict,
+                            (char *) (iface_data->hwaddr = g_steal_pointer(&v_hwaddr)),
                             iface_data->iface_get_config);
+    } else {
+        if (iface_data->iface_get_config->iface_idx >= 0) {
+            _LOGI("interface[%" G_GSSIZE_FORMAT "]: duplicate MAC address %s returned",
+                  iface_data->intern_iface_idx,
+                  iface_data->hwaddr);
+            error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN,
+                                       "duplicate MAC address for index %" G_GSSIZE_FORMAT,
+                                       iface_data->intern_iface_idx);
+            goto out_done;
+        }
     }
 
+    iface_data->iface_get_config->iface_idx = iface_data->extern_iface_idx;
+
     _LOGD("interface[%" G_GSSIZE_FORMAT "]: found a matching device with hwaddr %s",
-          iface_data->iface_idx,
+          iface_data->intern_iface_idx,
           iface_data->hwaddr);
 
-    nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT "/ipv4/ipAddress/", iface_data->iface_idx);
+    nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT "/ipv4/ipAddress/", iface_data->intern_iface_idx);
 
     nm_http_client_poll_get(NM_HTTP_CLIENT(source),
                             (uri = _azure_uri_interfaces(buf)),
@@ -385,36 +377,41 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data)
                             10000,
                             1000,
                             NM_MAKE_STRV(NM_AZURE_METADATA_HEADER),
-                            g_task_get_cancellable(azure_data->config_data->task),
+                            get_config_data->intern_cancellable,
                             NULL,
                             NULL,
                             _get_config_ips_prefix_list_cb,
                             iface_data);
     return;
 
-done:
-    nm_g_slice_free(iface_data);
-    --azure_data->n_ifaces_pending;
-    _get_config_maybe_task_return(azure_data, g_steal_pointer(&error));
+out_done:
+    --get_config_data->n_pending;
+    _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
 }
 
 static void
 _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
+    NMCSProviderGetConfigTaskData *get_config_data;
     gs_unref_ptrarray GPtrArray *ifaces_arr = NULL;
     gs_unref_bytes GBytes *response         = NULL;
     gs_free_error GError *error             = NULL;
-    AzureData *           azure_data        = user_data;
     const char *          response_str;
     gsize                 response_len;
     const char *          line;
     gsize                 line_len;
     guint                 i;
+    gssize                extern_iface_idx_cnt = 0;
 
     nm_http_client_poll_get_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error);
 
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    get_config_data = user_data;
+
     if (error) {
-        _get_config_maybe_task_return(azure_data, g_steal_pointer(&error));
+        _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
         return;
     }
 
@@ -422,31 +419,32 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
     /* NMHttpClient guarantees that there is a trailing NUL after the data. */
     nm_assert(response_str[response_len] == 0);
 
-    ifaces_arr = g_ptr_array_new();
+    ifaces_arr = g_ptr_array_new_with_free_func((GDestroyNotify) _azure_iface_data_destroy);
 
     while (nm_utils_parse_next_line(&response_str, &response_len, &line, &line_len)) {
         AzureIfaceData *iface_data;
-        gssize          iface_idx;
+        gssize          intern_iface_idx;
 
         if (line_len == 0)
             continue;
 
-        /* Truncate the string. It's safe to do, because we own @response_data an it has an
+        /* Truncate the string. It's safe to do, because we own @response an it has an
          * extra NULL character after the buffer. */
         ((char *) line)[line_len] = '\0';
 
         if (line[line_len - 1] == '/' && line_len != 0)
             ((char *) line)[--line_len] = '\0';
 
-        iface_idx = _nm_utils_ascii_str_to_int64(line, 10, 0, G_MAXSSIZE, -1);
-        if (iface_idx < 0)
+        intern_iface_idx = _nm_utils_ascii_str_to_int64(line, 10, 0, G_MAXSSIZE, -1);
+        if (intern_iface_idx < 0)
             continue;
 
         iface_data  = g_slice_new(AzureIfaceData);
         *iface_data = (AzureIfaceData){
+            .get_config_data      = get_config_data,
             .iface_get_config     = NULL,
-            .azure_data           = azure_data,
-            .iface_idx            = iface_idx,
+            .intern_iface_idx     = intern_iface_idx,
+            .extern_iface_idx     = extern_iface_idx_cnt++,
             .n_ips_prefix_pending = 0,
             .hwaddr               = NULL,
         };
@@ -456,21 +454,23 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
     _LOGD("found azure interfaces: %u", ifaces_arr->len);
 
     if (ifaces_arr->len == 0) {
-        error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "no Azure interfaces found");
-        _get_config_maybe_task_return(azure_data, g_steal_pointer(&error));
+        _nmcs_provider_get_config_task_maybe_return(
+            get_config_data,
+            nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "no Azure interfaces found"));
         return;
     }
 
     for (i = 0; i < ifaces_arr->len; ++i) {
-        AzureIfaceData *    data = ifaces_arr->pdata[i];
-        gs_free const char *uri  = NULL;
+        AzureIfaceData *    iface_data = ifaces_arr->pdata[i];
+        gs_free const char *uri        = NULL;
         char                buf[100];
 
-        _LOGD("azure interface[%" G_GSSIZE_FORMAT "]: retrieving configuration", data->iface_idx);
+        _LOGD("azure interface[%" G_GSSIZE_FORMAT "]: retrieving configuration",
+              iface_data->intern_iface_idx);
 
-        nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT "/macAddress", data->iface_idx);
+        nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT "/macAddress", iface_data->intern_iface_idx);
 
-        azure_data->n_ifaces_pending++;
+        get_config_data->n_pending++;
         nm_http_client_poll_get(NM_HTTP_CLIENT(source),
                                 (uri = _azure_uri_interfaces(buf)),
                                 HTTP_TIMEOUT_MS,
@@ -478,25 +478,21 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
                                 10000,
                                 1000,
                                 NM_MAKE_STRV(NM_AZURE_METADATA_HEADER),
-                                g_task_get_cancellable(azure_data->config_data->task),
+                                get_config_data->intern_cancellable,
                                 NULL,
                                 NULL,
                                 _get_config_iface_cb,
-                                data);
+                                iface_data);
     }
+
+    get_config_data->extra_data_destroy = (GDestroyNotify) g_ptr_array_unref;
+    get_config_data->extra_data         = g_steal_pointer(&ifaces_arr);
 }
 
 static void
 get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data)
 {
     gs_free const char *uri = NULL;
-    AzureData *         azure_data;
-
-    azure_data  = g_slice_new(AzureData);
-    *azure_data = (AzureData){
-        .config_data      = get_config_data,
-        .n_ifaces_pending = 0,
-    };
 
     nm_http_client_poll_get(nmcs_provider_get_http_client(provider),
                             (uri = _azure_uri_interfaces()),
@@ -505,11 +501,11 @@ get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_dat
                             15000,
                             1000,
                             NM_MAKE_STRV(NM_AZURE_METADATA_HEADER),
-                            g_task_get_cancellable(get_config_data->task),
+                            get_config_data->intern_cancellable,
                             NULL,
                             NULL,
                             _get_net_ifaces_list_cb,
-                            azure_data);
+                            get_config_data);
 }
 
 /*****************************************************************************/