diff options
Diffstat (limited to 'clients/cloud-setup')
| -rw-r--r-- | clients/cloud-setup/main.c | 4 | ||||
| -rw-r--r-- | clients/cloud-setup/meson.build | 52 | ||||
| -rw-r--r-- | clients/cloud-setup/nm-cloud-setup-utils.c | 87 | ||||
| -rw-r--r-- | clients/cloud-setup/nm-cloud-setup-utils.h | 32 | ||||
| -rw-r--r-- | clients/cloud-setup/nm-cloud-setup.service.in | 4 | ||||
| -rw-r--r-- | clients/cloud-setup/nm-http-client.c | 4 | ||||
| -rw-r--r-- | clients/cloud-setup/nm-http-client.h | 2 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider-azure.c | 342 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider-azure.h | 2 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider-ec2.c | 265 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider-ec2.h | 2 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider-gcp.c | 278 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider-gcp.h | 2 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider.c | 74 | ||||
| -rw-r--r-- | clients/cloud-setup/nmcs-provider.h | 42 | ||||
| -rw-r--r-- | clients/cloud-setup/tests/meson.build | 20 | ||||
| -rw-r--r-- | clients/cloud-setup/tests/test-cloud-setup-general.c | 117 |
17 files changed, 794 insertions, 535 deletions
diff --git a/clients/cloud-setup/main.c b/clients/cloud-setup/main.c index 1baf0feb..06590176 100644 --- a/clients/cloud-setup/main.c +++ b/clients/cloud-setup/main.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 "nm-libnm-aux/nm-libnm-aux.h" diff --git a/clients/cloud-setup/meson.build b/clients/cloud-setup/meson.build index c29d73df..581dde6a 100644 --- a/clients/cloud-setup/meson.build +++ b/clients/cloud-setup/meson.build @@ -1,6 +1,4 @@ -# SPDX-License-Identifier: LGPL-2.1+ - -name = 'nm-cloud-setup' +# SPDX-License-Identifier: LGPL-2.1-or-later if install_systemdunitdir @@ -23,30 +21,54 @@ if install_systemdunitdir endif -sources = files( - 'main.c', - 'nm-cloud-setup-utils.c', - 'nm-http-client.c', - 'nmcs-provider-ec2.c', - 'nmcs-provider-gcp.c', - 'nmcs-provider-azure.c', - 'nmcs-provider.c', +libnm_cloud_setup_core = static_library( + 'nm-cloud-setup-core', + sources: files( + 'nm-cloud-setup-utils.c', + 'nm-http-client.c', + 'nmcs-provider-ec2.c', + 'nmcs-provider-gcp.c', + 'nmcs-provider-azure.c', + 'nmcs-provider.c', + ), + dependencies: [ + libnmc_base_dep, + libnmc_dep, + libcurl_dep, + libnm_libnm_aux_dep, + ], + c_args: [ + '-DG_LOG_DOMAIN="nm-cloud-setup"', + ], +) + +libnm_cloud_setup_core_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: libnm_cloud_setup_core, ) executable( - name, - sources, + 'nm-cloud-setup', + files( + 'main.c', + ), dependencies: [ libnmc_base_dep, libnmc_dep, libcurl_dep, libnm_libnm_aux_dep, + libnm_cloud_setup_core_dep, + ], + c_args: [ + '-DG_LOG_DOMAIN="nm-cloud-setup"', ], - c_args: clients_c_flags + - ['-DG_LOG_DOMAIN="@0@"'.format(name)], link_with: libnm_systemd_logging_stub, link_args: ldflags_linker_script_binary, link_depends: linker_script_binary, install: true, install_dir: nm_libexecdir, ) + +if enable_tests + subdir('tests') +endif diff --git a/clients/cloud-setup/nm-cloud-setup-utils.c b/clients/cloud-setup/nm-cloud-setup-utils.c index 0820ed59..f5550a12 100644 --- a/clients/cloud-setup/nm-cloud-setup-utils.c +++ b/clients/cloud-setup/nm-cloud-setup-utils.c @@ -1,9 +1,12 @@ -/* 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 "nm-cloud-setup-utils.h" +#include <linux/if_ether.h> +#include <linux/if_infiniband.h> + #include "nm-glib-aux/nm-time-utils.h" #include "nm-glib-aux/nm-logging-base.h" #include "nm-glib-aux/nm-str-buf.h" @@ -482,22 +485,39 @@ char * nmcs_utils_hwaddr_normalize(const char *hwaddr, gssize len) { gs_free char *hwaddr_clone = NULL; + char * hw; guint8 buf[ETH_ALEN]; + gsize l; nm_assert(len >= -1); if (len < 0) { if (!hwaddr) return NULL; + l = strlen(hwaddr); } else { - if (len == 0) + l = len; + if (l > 0 && hwaddr[l - 1] == '\0') { + /* we accept one '\0' at the end of the string. */ + l--; + } + if (memchr(hwaddr, '\0', l)) { + /* but we don't accept other NUL characters in the middle. */ return NULL; - nm_assert(hwaddr); - hwaddr = nm_strndup_a(300, hwaddr, len, &hwaddr_clone); + } } + + if (l == 0) + return NULL; + + nm_assert(hwaddr); + hw = nm_strndup_a(300, hwaddr, l, &hwaddr_clone); + + g_strstrip(hw); + /* we cannot use _nm_utils_hwaddr_aton() because that requires a delimiter. * Azure exposes MAC addresses without delimiter, so accept that too. */ - if (!nm_utils_hexstr2bin_full(hwaddr, + if (!nm_utils_hexstr2bin_full(hw, FALSE, FALSE, FALSE, @@ -513,6 +533,59 @@ nmcs_utils_hwaddr_normalize(const char *hwaddr, gssize len) /*****************************************************************************/ +gboolean +nmcs_utils_ipaddr_normalize_bin(int addr_family, + const char *addr, + gssize len, + int * out_addr_family, + gpointer out_addr_bin) +{ + gs_free char *addr_clone = NULL; + char * ad; + gsize l; + + nm_assert(len >= -1); + + if (len < 0) { + if (!addr) + return FALSE; + l = strlen(addr); + } else { + l = len; + if (l > 0 && addr[l - 1] == '\0') { + /* we accept one '\0' at the end of the string. */ + l--; + } + if (memchr(addr, '\0', l)) { + /* but we don't accept other NUL characters in the middle. */ + return FALSE; + } + } + + if (l == 0) + return FALSE; + + nm_assert(addr); + ad = nm_strndup_a(300, addr, l, &addr_clone); + + g_strstrip(ad); + + return nm_utils_parse_inaddr_bin(addr_family, ad, out_addr_family, out_addr_bin); +} + +char * +nmcs_utils_ipaddr_normalize(int addr_family, const char *addr, gssize len) +{ + NMIPAddr ipaddr; + + if (!nmcs_utils_ipaddr_normalize_bin(addr_family, addr, len, &addr_family, &ipaddr)) + return NULL; + + return nm_utils_inet_ntop_dup(addr_family, &ipaddr); +} + +/*****************************************************************************/ + const char * nmcs_utils_parse_memmem(GBytes *mem, const char *needle) { @@ -631,7 +704,7 @@ nmcs_setting_ip_replace_ipv4_addresses(NMSettingIPConfig *s_ip, i_next++; } - if (any_changes) { + if (!any_changes) { while (i_next < num) { nm_setting_ip_config_remove_address(s_ip, --num); any_changes = TRUE; diff --git a/clients/cloud-setup/nm-cloud-setup-utils.h b/clients/cloud-setup/nm-cloud-setup-utils.h index 6980e752..52edd279 100644 --- a/clients/cloud-setup/nm-cloud-setup-utils.h +++ b/clients/cloud-setup/nm-cloud-setup-utils.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_CLOUD_SETUP_UTILS_H__ #define __NM_CLOUD_SETUP_UTILS_H__ @@ -78,6 +78,36 @@ gboolean nmcs_utils_poll_finish(GAsyncResult *result, gpointer *probe_user_data, char *nmcs_utils_hwaddr_normalize(const char *hwaddr, gssize len); +static inline char * +nmcs_utils_hwaddr_normalize_gbytes(GBytes *hwaddr) +{ + const char *str; + gsize len; + + str = g_bytes_get_data(hwaddr, &len); + return nmcs_utils_hwaddr_normalize(str, len); +} + +/*****************************************************************************/ + +gboolean nmcs_utils_ipaddr_normalize_bin(int addr_family, + const char *addr, + gssize len, + int * out_addr_family, + gpointer out_addr_bin); + +char *nmcs_utils_ipaddr_normalize(int addr_family, const char *addr, gssize len); + +static inline char * +nmcs_utils_ipaddr_normalize_gbytes(int addr_family, GBytes *addr) +{ + const char *str; + gsize len; + + str = g_bytes_get_data(addr, &len); + return nmcs_utils_ipaddr_normalize(addr_family, str, len); +} + /*****************************************************************************/ const char *nmcs_utils_parse_memmem(GBytes *mem, const char *needle); diff --git a/clients/cloud-setup/nm-cloud-setup.service.in b/clients/cloud-setup/nm-cloud-setup.service.in index 57f9e931..809f707d 100644 --- a/clients/cloud-setup/nm-cloud-setup.service.in +++ b/clients/cloud-setup/nm-cloud-setup.service.in @@ -1,5 +1,6 @@ [Unit] Description=Automatically configure NetworkManager in cloud +Documentation=man:nm-cloud-setup(8) After=NetworkManager.service [Service] @@ -11,6 +12,9 @@ ExecStart=@libexecdir@/nm-cloud-setup # Cloud providers are disabled by default. You need to # Opt-in by setting the right environment variable for # the provider. +# +# Create a drop-in file to overwrite these variables or +# use systemctl edit. #Environment=NM_CLOUD_SETUP_EC2=yes #Environment=NM_CLOUD_SETUP_GCP=yes #Environment=NM_CLOUD_SETUP_AZURE=yes diff --git a/clients/cloud-setup/nm-http-client.c b/clients/cloud-setup/nm-http-client.c index 221e8c71..144513fc 100644 --- a/clients/cloud-setup/nm-http-client.c +++ b/clients/cloud-setup/nm-http-client.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 "nm-http-client.h" diff --git a/clients/cloud-setup/nm-http-client.h b/clients/cloud-setup/nm-http-client.h index fc465e35..862b7338 100644 --- a/clients/cloud-setup/nm-http-client.h +++ b/clients/cloud-setup/nm-http-client.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NM_HTTP_CLIENT_C__ #define __NM_HTTP_CLIENT_C__ 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); } /*****************************************************************************/ diff --git a/clients/cloud-setup/nmcs-provider-azure.h b/clients/cloud-setup/nmcs-provider-azure.h index 849e94c1..be78768f 100644 --- a/clients/cloud-setup/nmcs-provider-azure.h +++ b/clients/cloud-setup/nmcs-provider-azure.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NMCS_PROVIDER_AZURE_H__ #define __NMCS_PROVIDER_AZURE_H__ diff --git a/clients/cloud-setup/nmcs-provider-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c index fb82914f..1e060034 100644 --- a/clients/cloud-setup/nmcs-provider-ec2.c +++ b/clients/cloud-setup/nmcs-provider-ec2.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-ec2.h" @@ -72,11 +72,11 @@ G_DEFINE_TYPE(NMCSProviderEC2, nmcs_provider_ec2, NMCS_TYPE_PROVIDER); static gboolean _detect_get_meta_data_check_cb(long response_code, - GBytes * response_data, + GBytes * response, 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, "ami-id"); } static void @@ -129,114 +129,69 @@ detect(NMCSProvider *provider, GTask *task) /*****************************************************************************/ -typedef struct { - NMCSProviderGetConfigTaskData *get_config_data; - GError * error; - GCancellable * cancellable; - gulong cancelled_id; - guint n_pending; -} GetConfigIfaceData; - static void -_get_config_task_maybe_return(GetConfigIfaceData *iface_data, GError *error_take) +_get_config_fetch_done_cb(NMHttpClient *http_client, + GAsyncResult *result, + gpointer user_data, + gboolean is_local_ipv4) { - 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); - } + NMCSProviderGetConfigTaskData *get_config_data; + const char * hwaddr = NULL; + gs_unref_bytes GBytes *response = NULL; + gs_free_error GError * error = NULL; + NMCSProviderGetConfigIfaceData *config_iface_data; + in_addr_t tmp_addr; + int tmp_prefix; - if (iface_data->n_pending > 0) - return; + nm_utils_user_data_unpack(user_data, &get_config_data, &hwaddr); - nm_clear_g_cancellable_disconnect(g_task_get_cancellable(get_config_data->task), - &iface_data->cancelled_id); + nm_http_client_poll_get_finish(http_client, result, NULL, &response, &error); - nm_clear_g_cancellable(&iface_data->cancellable); + if (nm_utils_error_is_cancelled(error)) + return; - 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); - } + if (error) + goto out; - nm_g_slice_free(iface_data); - g_object_unref(get_config_data->task); -} + config_iface_data = g_hash_table_lookup(get_config_data->result_dict, hwaddr); -static void -_get_config_fetch_done_cb(NMHttpClient *http_client, - GAsyncResult *result, - gpointer user_data, - gboolean is_local_ipv4) -{ - GetConfigIfaceData *iface_data; - const char * hwaddr = NULL; - gs_unref_bytes GBytes *response_data = NULL; - gs_free_error GError *error = NULL; + if (is_local_ipv4) { + gs_free const char **s_addrs = NULL; + gsize i, len; - nm_utils_user_data_unpack(user_data, &iface_data, &hwaddr); + s_addrs = nm_utils_strsplit_set_full(g_bytes_get_data(response, NULL), + "\n", + NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); + len = NM_PTRARRAY_LEN(s_addrs); - nm_http_client_poll_get_finish(http_client, result, NULL, &response_data, &error); + 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); - 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; + 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, 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)); +out: + get_config_data->n_pending--; + _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error)); } static void @@ -253,24 +208,6 @@ _get_config_fetch_done_cb_local_ipv4s(GObject *source, GAsyncResult *result, gpo _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) -{ - GetConfigIfaceData *iface_data = user_data; - - 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_maybe_return(iface_data, nm_utils_error_new_cancelled(FALSE, NULL)); -} - -typedef struct { - NMCSProviderGetConfigTaskData *get_config_data; - GHashTable * response_parsed; -} GetConfigMetadataData; - typedef struct { gssize iface_idx; char path[0]; @@ -279,60 +216,33 @@ typedef struct { static void _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; + NMCSProviderGetConfigTaskData *get_config_data; + gs_unref_hashtable GHashTable *response_parsed = NULL; + gs_free_error GError *error = NULL; 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)); + if (nm_utils_error_is_cancelled(error)) return; - } + + get_config_data = user_data; + + response_parsed = g_steal_pointer(&get_config_data->extra_data); + get_config_data->extra_data_destroy = NULL; /* 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, + _nmcs_provider_get_config_task_maybe_return( + get_config_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); @@ -359,6 +269,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us } 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)", @@ -366,7 +277,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us hwaddr, v_mac_data->path); - iface_data->n_pending++; + get_config_data->n_pending++; nm_http_client_poll_get( http_client, (uri1 = _ec2_uri_interfaces(v_mac_data->path, @@ -377,13 +288,13 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us 10000, 1000, NULL, - iface_data->cancellable, + get_config_data->intern_cancellable, NULL, NULL, _get_config_fetch_done_cb_subnet_ipv4_cidr_block, - nm_utils_user_data_pack(iface_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, hwaddr)); - iface_data->n_pending++; + get_config_data->n_pending++; nm_http_client_poll_get( http_client, (uri2 = _ec2_uri_interfaces(v_mac_data->path, @@ -394,23 +305,23 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us 10000, 1000, NULL, - iface_data->cancellable, + get_config_data->intern_cancellable, NULL, NULL, _get_config_fetch_done_cb_local_ipv4s, - nm_utils_user_data_pack(iface_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, hwaddr)); } - _get_config_task_maybe_return(iface_data, NULL); + _nmcs_provider_get_config_task_maybe_return(get_config_data, NULL); } static gboolean _get_config_metadata_ready_check(long response_code, - GBytes * response_data, + GBytes * response, gpointer check_user_data, GError **error) { - GetConfigMetadataData *metadata_data = check_user_data; + NMCSProviderGetConfigTaskData *get_config_data = check_user_data; gs_unref_hashtable GHashTable *response_parsed = NULL; const guint8 * r_data; const char * cur_line; @@ -421,12 +332,12 @@ _get_config_metadata_ready_check(long response_code, const char * c_hwaddr; gssize iface_idx_counter = 0; - if (response_code != 200 || !response_data) { + if (response_code != 200 || !response) { /* we wait longer. */ return FALSE; } - r_data = g_bytes_get_data(response_data, &r_len); + r_data = g_bytes_get_data(response, &r_len); /* NMHttpClient guarantees that there is a trailing NUL after the data. */ nm_assert(r_data[r_len] == 0); @@ -437,7 +348,7 @@ _get_config_metadata_ready_check(long response_code, if (cur_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 *) cur_line)[cur_line_len] = '\0'; @@ -454,11 +365,12 @@ _get_config_metadata_ready_check(long response_code, mac_data->iface_idx = iface_idx_counter++; memcpy(mac_data->path, cur_line, cur_line_len + 1u); + /* here we will ignore duplicate responses. */ 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); + g_hash_table_iter_init(&h_iter, 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; @@ -466,21 +378,18 @@ _get_config_metadata_ready_check(long response_code, } } - nm_clear_pointer(&metadata_data->response_parsed, g_hash_table_unref); - metadata_data->response_parsed = g_steal_pointer(&response_parsed); + nm_clear_pointer(&get_config_data->extra_data, g_hash_table_unref); + if (response_parsed) { + get_config_data->extra_data = g_steal_pointer(&response_parsed); + get_config_data->extra_data_destroy = (GDestroyNotify) g_hash_table_unref; + } return has_all; } static void 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, - }; + gs_free char *uri = NULL; /* 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 @@ -493,11 +402,11 @@ get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_dat 15000, 1000, NULL, - g_task_get_cancellable(get_config_data->task), + get_config_data->intern_cancellable, _get_config_metadata_ready_check, - metadata_data, + get_config_data, _get_config_metadata_ready_cb, - metadata_data); + get_config_data); } /*****************************************************************************/ diff --git a/clients/cloud-setup/nmcs-provider-ec2.h b/clients/cloud-setup/nmcs-provider-ec2.h index b5f178f5..8f8b6400 100644 --- a/clients/cloud-setup/nmcs-provider-ec2.h +++ b/clients/cloud-setup/nmcs-provider-ec2.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NMCS_PROVIDER_EC2_H__ #define __NMCS_PROVIDER_EC2_H__ 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); } /*****************************************************************************/ diff --git a/clients/cloud-setup/nmcs-provider-gcp.h b/clients/cloud-setup/nmcs-provider-gcp.h index 4ead2191..b0329935 100644 --- a/clients/cloud-setup/nmcs-provider-gcp.h +++ b/clients/cloud-setup/nmcs-provider-gcp.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NMCS_PROVIDER_GCP_H__ #define __NMCS_PROVIDER_GCP_H__ diff --git a/clients/cloud-setup/nmcs-provider.c b/clients/cloud-setup/nmcs-provider.c index e9e79a35..c700d8e1 100644 --- a/clients/cloud-setup/nmcs-provider.c +++ b/clients/cloud-setup/nmcs-provider.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.h" @@ -114,19 +114,61 @@ _iface_data_free(gpointer data) } static void -_get_config_data_free(gpointer data) +_get_config_task_maybe_return(NMCSProviderGetConfigTaskData *get_config_data, GError *error_take) { - NMCSProviderGetConfigTaskData *get_config_data = data; + gs_free_error GError *error = error_take; - if (get_config_data->extra_destroy) - get_config_data->extra_destroy(get_config_data->extra_data); + nm_assert(get_config_data); + nm_assert(G_IS_TASK(get_config_data->task)); + + if (!error) { + if (get_config_data->n_pending > 0) + return; + } + + g_cancellable_cancel(get_config_data->intern_cancellable); + + if (error) { + if (nm_utils_error_is_cancelled(error)) + _LOGD("get-config: cancelled"); + else + _LOGD("get-config: failed: %s", error->message); + g_task_return_error(get_config_data->task, g_steal_pointer(&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_clear_g_signal_handler(g_task_get_cancellable(get_config_data->task), + &get_config_data->extern_cancelled_id); + + if (get_config_data->extra_data_destroy) + get_config_data->extra_data_destroy(get_config_data->extra_data); nm_clear_pointer(&get_config_data->result_dict, g_hash_table_unref); + nm_g_object_unref(get_config_data->intern_cancellable); + g_object_unref(get_config_data->task); nm_g_slice_free(get_config_data); } void +_nmcs_provider_get_config_task_maybe_return(NMCSProviderGetConfigTaskData *get_config_data, + GError * error_take) +{ + nm_assert(!error_take || !nm_utils_error_is_cancelled(error_take)); + _get_config_task_maybe_return(get_config_data, error_take); +} + +static void +_get_config_cancelled_cb(GObject *object, gpointer user_data) +{ + _get_config_task_maybe_return(user_data, nm_utils_error_new_cancelled(FALSE, NULL)); +} + +void nmcs_provider_get_config(NMCSProvider * self, gboolean any, const char *const * hwaddrs, @@ -139,6 +181,8 @@ nmcs_provider_get_config(NMCSProvider * self, g_return_if_fail(NMCS_IS_PROVIDER(self)); g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + _LOGD("get-config: starting"); + get_config_data = g_slice_new(NMCSProviderGetConfigTaskData); *get_config_data = (NMCSProviderGetConfigTaskData){ .task = nm_g_task_new(self, cancellable, nmcs_provider_get_config, callback, user_data), @@ -146,8 +190,6 @@ nmcs_provider_get_config(NMCSProvider * self, .result_dict = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, _iface_data_free), }; - g_task_set_task_data(get_config_data->task, get_config_data, _get_config_data_free); - nmcs_wait_for_objects_register(get_config_data->task); for (; hwaddrs && hwaddrs[0]; hwaddrs++) { @@ -156,7 +198,21 @@ nmcs_provider_get_config(NMCSProvider * self, nmcs_provider_get_config_iface_data_new(TRUE)); } - _LOGD("get-config: starting"); + if (cancellable) { + gulong cancelled_id; + + cancelled_id = g_cancellable_connect(cancellable, + G_CALLBACK(_get_config_cancelled_cb), + get_config_data, + NULL); + if (cancelled_id == 0) { + /* the callback was already invoked synchronously and the task already returned. */ + return; + } + + get_config_data->extern_cancelled_id = cancelled_id; + get_config_data->intern_cancellable = g_cancellable_new(); + } NMCS_PROVIDER_GET_CLASS(self)->get_config(self, get_config_data); } diff --git a/clients/cloud-setup/nmcs-provider.h b/clients/cloud-setup/nmcs-provider.h index 876b0a37..3b0c2529 100644 --- a/clients/cloud-setup/nmcs-provider.h +++ b/clients/cloud-setup/nmcs-provider.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef __NMCS_PROVIDER_H__ #define __NMCS_PROVIDER_H__ @@ -12,11 +12,16 @@ typedef struct { in_addr_t *ipv4s_arr; gsize ipv4s_len; - gssize iface_idx; - in_addr_t cidr_addr; - guint8 cidr_prefix; - bool has_ipv4s : 1; - bool has_cidr : 1; + + /* If the interface was seen, get_config() should set this to a + * unique, increasing, positive index. If the interface is requested, + * it is initialized to -1. */ + gssize iface_idx; + + in_addr_t cidr_addr; + guint8 cidr_prefix; + bool has_ipv4s : 1; + bool has_cidr : 1; NMIPRoute **iproutes_arr; gsize iproutes_len; @@ -37,11 +42,25 @@ nmcs_provider_get_config_iface_data_is_valid(const NMCSProviderGetConfigIfaceDat NMCSProviderGetConfigIfaceData *nmcs_provider_get_config_iface_data_new(gboolean was_requested); typedef struct { - GTask * task; - GHashTable * result_dict; + GTask *task; + + GHashTable *result_dict; + + /* this cancellable should be used for the provider implementation + * to listen for cancellation. */ + GCancellable *intern_cancellable; + + /* the provider implementation may attach extra data. */ gpointer extra_data; - GDestroyNotify extra_destroy; - bool any : 1; + GDestroyNotify extra_data_destroy; + + gulong extern_cancelled_id; + + /* the provider implementation may use this field to track the number of pending + * operations. */ + guint n_pending; + + bool any : 1; } NMCSProviderGetConfigTaskData; #define NMCS_TYPE_PROVIDER (nmcs_provider_get_type()) @@ -93,6 +112,9 @@ gboolean nmcs_provider_detect_finish(NMCSProvider *provider, GAsyncResult *resul /*****************************************************************************/ +void _nmcs_provider_get_config_task_maybe_return(NMCSProviderGetConfigTaskData *get_config_data, + GError * error_take); + void nmcs_provider_get_config(NMCSProvider * provider, gboolean any, const char *const * hwaddrs, diff --git a/clients/cloud-setup/tests/meson.build b/clients/cloud-setup/tests/meson.build new file mode 100644 index 00000000..1c5e6ec6 --- /dev/null +++ b/clients/cloud-setup/tests/meson.build @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +exe = executable( + 'test-cloud-setup-general', + 'test-cloud-setup-general.c', + dependencies: [ + libnmc_base_dep, + libnmc_dep, + libnm_cloud_setup_core_dep, + ], + c_args: [ + '-DG_LOG_DOMAIN="test"', + ], +) + +test( + 'clients/cloud-setup/tests/test-cloud-setup-general', + test_script, + args: test_args + [exe.full_path()], +) diff --git a/clients/cloud-setup/tests/test-cloud-setup-general.c b/clients/cloud-setup/tests/test-cloud-setup-general.c new file mode 100644 index 00000000..63f3c13d --- /dev/null +++ b/clients/cloud-setup/tests/test-cloud-setup-general.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "libnm/nm-default-client.h" + +#include "nm-cloud-setup-utils.h" +#include "nm-libnm-core-intern/nm-libnm-core-utils.h" + +#include "nm-utils/nm-test-utils.h" + +/*****************************************************************************/ + +static NMSettingIPConfig * +_ri4a_new_s_ip(const char *const *addrs) +{ + NMSettingIPConfig *s_ip; + + s_ip = NM_SETTING_IP_CONFIG(nm_setting_ip4_config_new()); + + for (; addrs && addrs[0]; addrs++) { + nm_auto_unref_ip_address NMIPAddress *a = nmtst_ip_address_new(AF_INET, addrs[0]); + + nm_setting_ip_config_add_address(s_ip, a); + } + return s_ip; +} + +static GPtrArray * +_ri4a_new_arr(const char *const *addrs) +{ + GPtrArray *arr; + + arr = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); + + for (; addrs && addrs[0]; addrs++) + g_ptr_array_add(arr, nmtst_ip_address_new(AF_INET, addrs[0])); + + return arr; +} + +static void +_test_ri4a(const char *const *addrs_before, const char *const *addrs_new) +{ + gs_unref_object NMSettingIPConfig *s_ip = NULL; + gs_unref_ptrarray GPtrArray *arr = NULL; + const gsize n = NM_PTRARRAY_LEN(addrs_new); + gboolean changed; + gsize i; + + s_ip = _ri4a_new_s_ip(addrs_before); + arr = _ri4a_new_arr(addrs_new); + + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip), + ==, + NM_PTRARRAY_LEN(addrs_before)); + g_assert_cmpint(arr->len, ==, n); + + changed = nmcs_setting_ip_replace_ipv4_addresses(s_ip, (NMIPAddress **) arr->pdata, arr->len); + + g_assert_cmpint(changed, !=, nm_utils_strv_equal(addrs_before, addrs_new)); + g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip), ==, n); + + for (i = 0; i < n; i++) { + NMIPAddress *a = arr->pdata[i]; + NMIPAddress *b = nm_setting_ip_config_get_address(s_ip, i); + + if (nmtst_get_rand_bool()) + NM_SWAP(&a, &b); + + g_assert(nm_ip_address_cmp_full(a, b, NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS) == 0); + } +} + +static void +test_replace_ipv4_addresses(void) +{ + const char *const *const LISTS[] = { + NM_MAKE_STRV(), + NM_MAKE_STRV("192.168.5.1/24"), + NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24"), + NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24", "192.168.5.3/24"), + NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24", "192.168.5.3/24", "192.168.5.4/24"), + }; + int i_run; + + for (i_run = 0; i_run < 20; i_run++) { + gs_free const char **addrs_before = NULL; + gs_free const char **addrs_new = NULL; + + addrs_before = nmtst_rand_perm_strv(LISTS[nmtst_get_rand_uint32() % G_N_ELEMENTS(LISTS)]); + addrs_new = nmtst_rand_perm_strv(LISTS[nmtst_get_rand_uint32() % G_N_ELEMENTS(LISTS)]); + + _test_ri4a(addrs_before, addrs_new); + } + + _test_ri4a(NM_MAKE_STRV(), NM_MAKE_STRV()); + _test_ri4a(NM_MAKE_STRV(), NM_MAKE_STRV("192.168.5.1/24")); + _test_ri4a(NM_MAKE_STRV(), NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24")); + _test_ri4a(NM_MAKE_STRV("192.168.5.1/24"), NM_MAKE_STRV()); + _test_ri4a(NM_MAKE_STRV("192.168.5.1/24"), NM_MAKE_STRV("192.168.5.1/24")); + _test_ri4a(NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24"), NM_MAKE_STRV()); + _test_ri4a(NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24"), NM_MAKE_STRV("192.168.5.1/24")); + _test_ri4a(NM_MAKE_STRV("192.168.5.1/24", "192.168.5.2/24"), NM_MAKE_STRV("192.168.5.2/24")); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init(&argc, &argv, TRUE); + + g_test_add_func("/cloud-setup/general/replace-ipv4-addresses", test_replace_ipv4_addresses); + + return g_test_run(); +} |