about summary refs log tree commit diff
path: root/clients/cloud-setup/nmcs-provider-gcp.c
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2021-02-25 11:35:01 +0100
committerSebastien Bacher <seb128@ubuntu.com>2021-02-25 11:49:48 +0100
commit77dea84aa6e816f66dc622cec8ae2668eba3c068 (patch)
tree755536a0ed1fb36c2899e9bff9e092c4008a25c4 /clients/cloud-setup/nmcs-provider-gcp.c
parent47f08826b44eed651b44dc8bacb5b46bc4c52067 (diff)
parentfd80297a396cc4dd7da4da20208fb360aa369aa7 (diff)
Merge branch 'debian/master' into ubuntu/master
Diffstat (limited to 'clients/cloud-setup/nmcs-provider-gcp.c')
-rw-r--r--clients/cloud-setup/nmcs-provider-gcp.c278
1 files changed, 144 insertions, 134 deletions
diff --git a/clients/cloud-setup/nmcs-provider-gcp.c b/clients/cloud-setup/nmcs-provider-gcp.c
index b4398fb8..1deaea10 100644
--- a/clients/cloud-setup/nmcs-provider-gcp.c
+++ b/clients/cloud-setup/nmcs-provider-gcp.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-gcp.h"
 
@@ -89,126 +89,97 @@ detect(NMCSProvider *provider, GTask *task)
 /*****************************************************************************/
 
 typedef struct {
-    NMCSProviderGetConfigTaskData *config_data;
-    guint                          n_ifaces_pending;
-    GError *                       error;
-} GCPData;
-
-typedef struct {
+    NMCSProviderGetConfigTaskData * get_config_data;
     NMCSProviderGetConfigIfaceData *iface_get_config;
-    GCPData *                       gcp_data;
-    gssize                          iface_idx;
+    gssize                          intern_iface_idx;
+    gssize                          extern_iface_idx;
     guint                           n_fips_pending;
 } GCPIfaceData;
 
 static void
-_get_config_maybe_task_return(GCPData *gcp_data, GError *error_take)
+_gcp_iface_data_destroy(GCPIfaceData *iface_data)
 {
-    NMCSProviderGetConfigTaskData *config_data = gcp_data->config_data;
-
-    if (error_take) {
-        if (!gcp_data->error)
-            gcp_data->error = error_take;
-        else if (!nm_utils_error_is_cancelled(gcp_data->error)
-                 && nm_utils_error_is_cancelled(error_take)) {
-            nm_clear_error(&gcp_data->error);
-            gcp_data->error = error_take;
-        } else
-            g_error_free(error_take);
-    }
-
-    if (gcp_data->n_ifaces_pending > 0)
-        return;
-
-    if (gcp_data->error) {
-        if (nm_utils_error_is_cancelled(gcp_data->error))
-            _LOGD("get-config: cancelled");
-        else
-            _LOGD("get-config: failed: %s", gcp_data->error->message);
-        g_task_return_error(config_data->task, g_steal_pointer(&gcp_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);
-    }
-
-    nm_g_slice_free(gcp_data);
-    g_object_unref(config_data->task);
+    nm_g_slice_free(iface_data);
 }
 
 static void
 _get_config_fip_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
+    NMCSProviderGetConfigTaskData * get_config_data;
     NMCSProviderGetConfigIfaceData *iface_get_config;
     gs_unref_bytes GBytes *response   = NULL;
     GCPIfaceData *         iface_data = user_data;
     gs_free_error GError *error       = NULL;
-    const char *          fip_str     = NULL;
+    gs_free char *        ipaddr      = NULL;
     NMIPRoute **          routes_arr;
     NMIPRoute *           route_new;
-    GCPData *             gcp_data;
-
-    gcp_data = iface_data->gcp_data;
 
     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 iface_done;
+        goto out_done;
 
-    fip_str = g_bytes_get_data(response, NULL);
-    if (!nm_utils_ipaddr_valid(AF_INET, fip_str)) {
+    ipaddr = nmcs_utils_ipaddr_normalize_gbytes(AF_INET, response);
+    if (!ipaddr) {
         error =
             nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "forwarded-ip is not a valid ip address");
-        goto iface_done;
+        goto out_done;
     }
 
     _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: adding forwarded-ip %s",
-          iface_data->iface_idx,
-          fip_str);
+          iface_data->intern_iface_idx,
+          ipaddr);
 
-    iface_get_config            = iface_data->iface_get_config;
-    iface_get_config->iface_idx = iface_data->iface_idx;
-    routes_arr                  = iface_get_config->iproutes_arr;
+    iface_get_config = iface_data->iface_get_config;
+    routes_arr       = iface_get_config->iproutes_arr;
 
-    route_new = nm_ip_route_new(AF_INET, fip_str, 32, NULL, 100, &error);
+    route_new = nm_ip_route_new(AF_INET, ipaddr, 32, NULL, 100, &error);
     if (error)
-        goto iface_done;
+        goto out_done;
 
     nm_ip_route_set_attribute(route_new, NM_IP_ROUTE_ATTRIBUTE_TYPE, g_variant_new_string("local"));
     routes_arr[iface_get_config->iproutes_len] = route_new;
     ++iface_get_config->iproutes_len;
 
-iface_done:
-    --iface_data->n_fips_pending;
-    if (iface_data->n_fips_pending == 0) {
-        nm_g_slice_free(iface_data);
-        --gcp_data->n_ifaces_pending;
+out_done:
+    if (!error) {
+        --iface_data->n_fips_pending;
+        if (iface_data->n_fips_pending > 0)
+            return;
     }
 
-    _get_config_maybe_task_return(gcp_data, g_steal_pointer(&error));
+    --get_config_data->n_pending;
+    _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
 }
 
 static void
 _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
+    NMCSProviderGetConfigTaskData *get_config_data;
     gs_unref_ptrarray GPtrArray *uri_arr = NULL;
     gs_unref_bytes GBytes *response      = NULL;
     GCPIfaceData *         iface_data    = user_data;
     gs_free_error GError *error          = NULL;
     const char *          response_str   = NULL;
     gsize                 response_len;
-    GCPData *             gcp_data;
     const char *          line;
     gsize                 line_len;
     guint                 i;
 
-    gcp_data = iface_data->gcp_data;
-
     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 fips_error;
+        goto out_error;
 
     response_str = g_bytes_get_data(response, &response_len);
     /* NMHttpClient guarantees that there is a trailing NUL after the data. */
@@ -218,7 +189,7 @@ _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
     while (nm_utils_parse_next_line(&response_str, &response_len, &line, &line_len)) {
         gint64 fip_index;
 
-        /* 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 NUL character after the buffer. */
         ((char *) line)[line_len] = '\0';
 
@@ -228,19 +199,19 @@ _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
 
         g_ptr_array_add(uri_arr,
                         g_strdup_printf("%" G_GSSIZE_FORMAT "/forwarded-ips/%" G_GINT64_FORMAT,
-                                        iface_data->iface_idx,
+                                        iface_data->intern_iface_idx,
                                         fip_index));
     }
 
     iface_data->n_fips_pending = uri_arr->len;
 
     _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: found %u forwarded ips",
-          iface_data->iface_idx,
+          iface_data->intern_iface_idx,
           iface_data->n_fips_pending);
 
     if (iface_data->n_fips_pending == 0) {
         error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "found no forwarded ip");
-        goto fips_error;
+        goto out_error;
     }
 
     iface_data->iface_get_config->iproutes_arr = g_new(NMIPRoute *, iface_data->n_fips_pending);
@@ -256,7 +227,7 @@ _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
                                 HTTP_POLL_TIMEOUT_MS,
                                 HTTP_RATE_LIMIT_MS,
                                 NM_MAKE_STRV(NM_GCP_METADATA_HEADER),
-                                g_task_get_cancellable(gcp_data->config_data->task),
+                                get_config_data->intern_cancellable,
                                 NULL,
                                 NULL,
                                 _get_config_fip_cb,
@@ -264,45 +235,80 @@ _get_config_ips_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
     }
     return;
 
-fips_error:
-    nm_g_slice_free(iface_data);
-    --gcp_data->n_ifaces_pending;
-    _get_config_maybe_task_return(gcp_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)
 {
-    gs_unref_bytes GBytes *response   = NULL;
-    GCPIfaceData *         iface_data = user_data;
-    gs_free_error GError *error       = NULL;
-    gs_free const char *  hwaddr      = NULL;
-    gs_free const char *  uri         = NULL;
-    char                  sbuf[100];
-    GCPData *             gcp_data;
-
-    gcp_data = iface_data->gcp_data;
+    gs_unref_bytes GBytes *response         = NULL;
+    GCPIfaceData *         iface_data       = user_data;
+    gs_free_error GError *         error    = NULL;
+    gs_free char *                 v_hwaddr = NULL;
+    const char *                   hwaddr   = NULL;
+    gs_free const char *           uri      = NULL;
+    char                           sbuf[100];
+    NMCSProviderGetConfigTaskData *get_config_data;
+    gboolean                       is_requested;
 
     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 iface_error;
+        goto out_done;
 
-    hwaddr = nmcs_utils_hwaddr_normalize(g_bytes_get_data(response, NULL), -1);
-    iface_data->iface_get_config = g_hash_table_lookup(gcp_data->config_data->result_dict, hwaddr);
-    if (!iface_data->iface_get_config) {
-        _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: did not find a matching device",
-              iface_data->iface_idx);
+    v_hwaddr = nmcs_utils_hwaddr_normalize_gbytes(response);
+    if (!v_hwaddr) {
+        _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: invalid MAC address returned",
+              iface_data->intern_iface_idx);
         error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN,
-                                   "no matching hwaddr found for GCP interface");
-        goto iface_error;
+                                   "invalid MAC address for index %" G_GSSIZE_FORMAT,
+                                   iface_data->intern_iface_idx);
+        goto out_done;
+    }
+
+    if (!g_hash_table_lookup_extended(get_config_data->result_dict,
+                                      v_hwaddr,
+                                      (gpointer *) &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(get_config_data->result_dict,
+                            (char *) (hwaddr = g_steal_pointer(&v_hwaddr)),
+                            iface_data->iface_get_config);
+        is_requested = FALSE;
+    } else {
+        if (iface_data->iface_get_config->iface_idx >= 0) {
+            _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: duplicate MAC address %s returned",
+                  iface_data->intern_iface_idx,
+                  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;
+        }
+        is_requested = TRUE;
     }
 
-    _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: found a matching device with hwaddr %s",
-          iface_data->iface_idx,
+    iface_data->iface_get_config->iface_idx = iface_data->extern_iface_idx;
+
+    _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: found a %sdevice with hwaddr %s",
+          iface_data->intern_iface_idx,
+          is_requested ? "requested " : "",
           hwaddr);
 
-    nm_sprintf_buf(sbuf, "%" G_GSSIZE_FORMAT "/forwarded-ips/", iface_data->iface_idx);
+    nm_sprintf_buf(sbuf, "%" G_GSSIZE_FORMAT "/forwarded-ips/", iface_data->intern_iface_idx);
 
     nm_http_client_poll_get(NM_HTTP_CLIENT(source),
                             (uri = _gcp_uri_interfaces(sbuf)),
@@ -311,17 +317,16 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data)
                             HTTP_POLL_TIMEOUT_MS,
                             HTTP_RATE_LIMIT_MS,
                             NM_MAKE_STRV(NM_GCP_METADATA_HEADER),
-                            g_task_get_cancellable(gcp_data->config_data->task),
+                            get_config_data->intern_cancellable,
                             NULL,
                             NULL,
                             _get_config_ips_list_cb,
                             iface_data);
     return;
 
-iface_error:
-    nm_g_slice_free(iface_data);
-    --gcp_data->n_ifaces_pending;
-    _get_config_maybe_task_return(gcp_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
@@ -329,18 +334,24 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
 {
     gs_unref_ptrarray GPtrArray *ifaces_arr = NULL;
     gs_unref_bytes GBytes *response         = NULL;
-    gs_free_error GError *error             = NULL;
-    GCPData *             gcp_data          = user_data;
-    const char *          response_str;
-    gsize                 response_len;
-    const char *          line;
-    gsize                 line_len;
-    guint                 i;
+    gs_free_error GError *         error    = NULL;
+    NMCSProviderGetConfigTaskData *get_config_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(gcp_data, g_steal_pointer(&error));
+        _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
         return;
     }
 
@@ -348,47 +359,56 @@ _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) _gcp_iface_data_destroy);
 
     while (nm_utils_parse_next_line(&response_str, &response_len, &line, &line_len)) {
         GCPIfaceData *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 NUL character after the buffer. */
         ((char *) line)[line_len] = '\0';
         if (line[line_len - 1] == '/')
             ((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(GCPIfaceData);
         *iface_data = (GCPIfaceData){
+            .get_config_data  = get_config_data,
             .iface_get_config = NULL,
-            .gcp_data         = gcp_data,
-            .iface_idx        = iface_idx,
+            .intern_iface_idx = intern_iface_idx,
+            .extern_iface_idx = extern_iface_idx_cnt++,
             .n_fips_pending   = 0,
         };
         g_ptr_array_add(ifaces_arr, iface_data);
     }
 
-    gcp_data->n_ifaces_pending = ifaces_arr->len;
     _LOGI("found GCP interfaces: %u", ifaces_arr->len);
 
+    if (ifaces_arr->len == 0) {
+        _nmcs_provider_get_config_task_maybe_return(
+            get_config_data,
+            nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "no GCP interfaces found"));
+        return;
+    }
+
     for (i = 0; i < ifaces_arr->len; ++i) {
         GCPIfaceData *      data = ifaces_arr->pdata[i];
         gs_free const char *uri  = NULL;
         char                sbuf[100];
 
-        _LOGD("GCP interface[%" G_GSSIZE_FORMAT "]: retrieving configuration", data->iface_idx);
+        _LOGD("GCP interface[%" G_GSSIZE_FORMAT "]: retrieving configuration",
+              data->intern_iface_idx);
 
-        nm_sprintf_buf(sbuf, "%" G_GSSIZE_FORMAT "/mac", data->iface_idx);
+        nm_sprintf_buf(sbuf, "%" G_GSSIZE_FORMAT "/mac", data->intern_iface_idx);
 
+        get_config_data->n_pending++;
         nm_http_client_poll_get(NM_HTTP_CLIENT(source),
                                 (uri = _gcp_uri_interfaces(sbuf)),
                                 HTTP_TIMEOUT_MS,
@@ -396,31 +416,21 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
                                 HTTP_POLL_TIMEOUT_MS,
                                 HTTP_RATE_LIMIT_MS,
                                 NM_MAKE_STRV(NM_GCP_METADATA_HEADER),
-                                g_task_get_cancellable(gcp_data->config_data->task),
+                                get_config_data->intern_cancellable,
                                 NULL,
                                 NULL,
                                 _get_config_iface_cb,
                                 data);
     }
 
-    if (ifaces_arr->len == 0) {
-        error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "no GCP interfaces found");
-        _get_config_maybe_task_return(gcp_data, g_steal_pointer(&error));
-    }
+    get_config_data->extra_data         = g_steal_pointer(&ifaces_arr);
+    get_config_data->extra_data_destroy = (GDestroyNotify) g_ptr_array_unref;
 }
 
 static void
 get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data)
 {
     gs_free const char *uri = NULL;
-    GCPData *           gcp_data;
-
-    gcp_data  = g_slice_new(GCPData);
-    *gcp_data = (GCPData){
-        .config_data      = get_config_data,
-        .n_ifaces_pending = 0,
-        .error            = NULL,
-    };
 
     nm_http_client_poll_get(nmcs_provider_get_http_client(provider),
                             (uri = _gcp_uri_interfaces()),
@@ -429,11 +439,11 @@ get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_dat
                             HTTP_POLL_TIMEOUT_MS,
                             HTTP_RATE_LIMIT_MS,
                             NM_MAKE_STRV(NM_GCP_METADATA_HEADER),
-                            g_task_get_cancellable(gcp_data->config_data->task),
+                            get_config_data->intern_cancellable,
                             NULL,
                             NULL,
                             _get_net_ifaces_list_cb,
-                            gcp_data);
+                            get_config_data);
 }
 
 /*****************************************************************************/