summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2021-01-08 16:37:57 +0100
committerThomas Haller <thaller@redhat.com>2021-01-08 16:38:24 +0100
commit395273d20d2310e60f629af47335a64fbcbc895f (patch)
treed491bb51fee8779448021abeae96a69f0aa46466
parentfc6475bbf7b17f0c9ac9b44f9acfbb9cbd62c9bd (diff)
parentf0c4b3b287ff08af4aa0a34cee2735836d6bdd70 (diff)
downloadNetworkManager-395273d20d2310e60f629af47335a64fbcbc895f.tar.gz
cloud-setup: merge branch 'th/cloud-setup-man'
https://bugzilla.redhat.com/show_bug.cgi?id=1867997 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/600 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/719
-rw-r--r--clients/cloud-setup/nm-cloud-setup-utils.c78
-rw-r--r--clients/cloud-setup/nm-cloud-setup-utils.h30
-rw-r--r--clients/cloud-setup/nmcs-provider-azure.c338
-rw-r--r--clients/cloud-setup/nmcs-provider-ec2.c268
-rw-r--r--clients/cloud-setup/nmcs-provider-gcp.c274
-rw-r--r--clients/cloud-setup/nmcs-provider.c70
-rw-r--r--clients/cloud-setup/nmcs-provider.h40
-rw-r--r--man/nm-cloud-setup.xml100
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c15
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h2
10 files changed, 699 insertions, 516 deletions
diff --git a/clients/cloud-setup/nm-cloud-setup-utils.c b/clients/cloud-setup/nm-cloud-setup-utils.c
index 38eb0bcf72..95a84d10f6 100644
--- a/clients/cloud-setup/nm-cloud-setup-utils.c
+++ b/clients/cloud-setup/nm-cloud-setup-utils.c
@@ -485,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,
@@ -516,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)
{
diff --git a/clients/cloud-setup/nm-cloud-setup-utils.h b/clients/cloud-setup/nm-cloud-setup-utils.h
index c2a3d30949..52edd27994 100644
--- a/clients/cloud-setup/nm-cloud-setup-utils.h
+++ b/clients/cloud-setup/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/nmcs-provider-azure.c b/clients/cloud-setup/nmcs-provider-azure.c
index a33fc3e0d1..e55ecc9d28 100644
--- a/clients/cloud-setup/nmcs-provider-azure.c
+++ b/clients/cloud-setup/nmcs-provider-azure.c
@@ -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-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c
index 40810757de..49b909bf77 100644
--- a/clients/cloud-setup/nmcs-provider-ec2.c
+++ b/clients/cloud-setup/nmcs-provider-ec2.c
@@ -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,124 +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);
-
- nm_clear_g_cancellable(&iface_data->cancellable);
- }
+ 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;
+ }
}
- /* If nm_utils_error_is_cancelled(error), then our internal iface_data->cancellable
- * was cancelled, because the overall request failed. From point of view of the
- * caller, this does not mean that a cancellation happened. It also means, our
- * request overall is already about to fail. */
- nm_assert(!nm_utils_error_is_cancelled(error) || iface_data->error);
-
- iface_data->n_pending--;
- _get_config_task_maybe_return(iface_data,
- nm_utils_error_is_cancelled(error) ? NULL
- : 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
@@ -263,21 +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;
-
- 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];
@@ -286,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) {
- /* the callback was already invoked synchronously and the task already returned. */
- 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);
@@ -366,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)",
@@ -373,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,
@@ -384,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,
@@ -401,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;
@@ -428,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);
@@ -444,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';
@@ -461,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;
@@ -473,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
@@ -500,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-gcp.c b/clients/cloud-setup/nmcs-provider-gcp.c
index 09b09d0f67..425621b43f 100644
--- a/clients/cloud-setup/nmcs-provider-gcp.c
+++ b/clients/cloud-setup/nmcs-provider-gcp.c
@@ -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.c b/clients/cloud-setup/nmcs-provider.c
index 9fac4b6679..6512b76faf 100644
--- a/clients/cloud-setup/nmcs-provider.c
+++ b/clients/cloud-setup/nmcs-provider.c
@@ -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 a26c6a366b..3b0c2529ed 100644
--- a/clients/cloud-setup/nmcs-provider.h
+++ b/clients/cloud-setup/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/man/nm-cloud-setup.xml b/man/nm-cloud-setup.xml
index 16c695a97f..2ffee5b055 100644
--- a/man/nm-cloud-setup.xml
+++ b/man/nm-cloud-setup.xml
@@ -49,11 +49,12 @@
desirable to automatically configure the network of that VM.
In simple setups, the VM only has one network interface and the public
cloud supports automatic configuration via DHCP, DHCP6 or IPv6 autoconf.
- However, on the virtual machine might have multiple network
+ However, the virtual machine might have multiple network
interfaces, or multiple IP addresses and IP subnets
- on one interface. Also, the administrator can reconfigure those settings
- while the machine is running. NetworkManager's nm-cloud-setup is a tool
- that automatically picks up such configuration and updates the network
+ on one interface which cannot be configured via DHCP. Also, the administrator
+ may reconfigure the network while the machine is running. NetworkManager's
+ nm-cloud-setup is a tool
+ that automatically picks up such configuration in cloud environments and updates the network
configuration of the host.</para>
<para>Multiple cloud providers are supported. See <xref linkend="providers"/>.</para>
@@ -87,6 +88,11 @@
<command>nmcli connection add type ethernet ...</command>.
</para>
+ <para>
+ By setting the user-data <literal>org.freedesktop.nm-cloud-setup.skip=yes</literal>
+ on the profile, nm-cloud-setup will skip the device.
+ </para>
+
<para>nm-cloud-setup modifies the run time configuration akin to <command>nmcli device modify</command>.
With this approach, the configuration is not persisted
and only preserved until the device disconnects.</para>
@@ -119,7 +125,7 @@
<para>Usually <command>/usr/libexec/nm-cloud-setup</command> is not run directly,
but only by <command>systemctl restart nm-cloud-setup.service</command>. This
ensures that the tool only runs once at any time. It also allows to integrate
- use the nm-cloud-setup systemd timer,
+ with the nm-cloud-setup systemd timer,
and to enable/disable the service via systemd.</para>
<para>As you need to set environment variable to configure nm-cloud-setup binary,
@@ -161,7 +167,7 @@
<itemizedlist>
<listitem>
<para><literal>NM_CLOUD_SETUP_LOG</literal>: control the logging verbosity. Set it
- one of <literal>TRACE</literal>, <literal>DEBUG</literal>, <literal>INFO</literal>,
+ to one of <literal>TRACE</literal>, <literal>DEBUG</literal>, <literal>INFO</literal>,
<literal>WARN</literal>, <literal>ERR</literal> or <literal>OFF</literal>. The program
will print message on stdout and the default level is <literal>WARN</literal>.</para>
</listitem>
@@ -187,7 +193,7 @@
<refsect2>
<title>Amazon EC2 (AWS)</title>
- <para>The tools tries to fetch configuration from <literal>http://169.254.169.254/</literal>. Currently, it only
+ <para>For AWS, the tools tries to fetch configuration from <literal>http://169.254.169.254/</literal>. Currently, it only
configures IPv4 and does nothing about IPv6. It will do the following.</para>
<itemizedlist>
@@ -233,7 +239,7 @@
<command>nmcli device modify "eth0" ipv4.addresses "172.16.5.3/24,172.16.5.4/24" ipv4.routes "0.0.0.0/0 172.16.5.1 10 table=30401" ipv4.routing-rules "priority 30401 from 172.16.5.3/32 table 30401, priority 30401 from 172.16.5.4/32 table 30401"</command>.
Note that this replaces the previous addresses, routes and rules with the new information.
But also note that this only changes the run time configuration of the device. The
- connection profile is not affected by that.
+ connection profile on disk is not affected.
</para>
</listitem>
</itemizedlist>
@@ -242,13 +248,87 @@
<refsect2>
<title>Google Cloud Platform (GCP)</title>
- <para>The tools tries to fetch configuration from <literal>http://metadata.google.internal/</literal>.</para>
+ <para>
+ For GCP, the meta data is fetched from URIs starting with <literal>http://metadata.google.internal/computeMetadata/v1/</literal> with a
+ HTTP header <literal>"Metadata-Flavor: Google"</literal>.
+ Currently, the tool only configures IPv4 and does nothing about IPv6. It will do the following.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>First fetch <literal>http://metadata.google.internal/computeMetadata/v1/instance/id</literal> to detect whether the tool
+ runs on Google Cloud Platform. Only if the platform is detected, it will continue fetching the configuration.</para>
+ </listitem>
+ <listitem>
+ <para>Fetch <literal>http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/</literal> to get the list
+ of available interface indexes. These indexes can be used for further lookups.</para>
+ </listitem>
+ <listitem>
+ <para>Then, for each interface fetch <literal>http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/$IFACE_INDEX/mac</literal> to get
+ the corresponding MAC address of the found interfaces. The MAC address is used to identify the device later on.</para>
+ </listitem>
+ <listitem>
+ <para>Then, for each interface with a MAC address fetch <literal>http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/$IFACE_INDEX/forwarded-ips/</literal>
+ and then all the found IP addresses at <literal>http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/$IFACE_INDEX/forwarded-ips/$FIPS_INDEX</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>At this point, we have a list of all interfaces (by MAC address) and their configured IPv4 addresses.</para>
+ <para>For each device, we lookup the currently applied connection in NetworkManager. That implies, that the device is currently activated
+ in NetworkManager. If no such device was in NetworkManager, or if the profile has user-data <literal>org.freedesktop.nm-cloud-setup.skip=yes</literal>,
+ we skip the device. Now for each found IP address we add a static route "$FIPS_ADDR/32 0.0.0.0 100 type=local" and reapply the change.</para>
+ <para>The effect is not unlike calling <command>nmcli device modify "$DEVICE" ipv4.routes "$FIPS_ADDR/32 0.0.0.0 100 type=local [,...]"</command> for all relevant
+ devices and all found addresses.</para>
+ </listitem>
+ </itemizedlist>
</refsect2>
<refsect2>
<title>Microsoft Azure</title>
- <para>The tools tries to fetch configuration from <literal>http://169.254.169.254/</literal>.</para>
+ <para>
+ For Azure, the meta data is fetched from URIs starting with <literal>http://169.254.169.254/metadata/instance</literal> with a
+ URL parameter <literal>"?format=text&amp;api-version=2017-04-02"</literal> and a HTTP header <literal>"Metadata:true"</literal>.
+ Currently, the tool only configures IPv4 and does nothing about IPv6. It will do the following.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>First fetch <literal>http://169.254.169.254/metadata/instance?format=text&amp;api-version=2017-04-02</literal> to detect whether the tool
+ runs on Azure Cloud. Only if the platform is detected, it will continue fetching the configuration.</para>
+ </listitem>
+ <listitem>
+ <para>Fetch <literal>http://169.254.169.254/metadata/instance/network/interface/?format=text&amp;api-version=2017-04-02</literal> to get the list
+ of available interface indexes. These indexes can be used for further lookups.</para>
+ </listitem>
+ <listitem>
+ <para>Then, for each interface fetch <literal>http://169.254.169.254/metadata/instance/network/interface/$IFACE_INDEX/macAddress?format=text&amp;api-version=2017-04-02</literal>
+ to get the corresponding MAC address of the found interfaces. The MAC address is used to identify the device later on.</para>
+ </listitem>
+ <listitem>
+ <para>Then, for each interface with a MAC address fetch <literal>http://169.254.169.254/metadata/instance/network/interface/$IFACE_INDEX/ipv4/ipAddress/?format=text&amp;api-version=2017-04-02</literal>
+ to get the list of (indexes of) IP addresses on that interface.
+ </para>
+ </listitem>
+ <listitem>
+ <para>Then, for each IP address index fetch the address at
+ <literal>http://169.254.169.254/metadata/instance/network/interface/$IFACE_INDEX/ipv4/ipAddress/$ADDR_INDEX/privateIpAddress?format=text&amp;api-version=2017-04-02</literal>.
+ Also fetch the size of the subnet (the netmask) for the interface from
+ <literal>http://169.254.169.254/metadata/instance/network/interface/$IFACE_INDEX/ipv4/subnet/0/prefix/?format=text&amp;api-version=2017-04-02</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>At this point, we have a list of all interfaces (by MAC address) and their configured IPv4 addresses.</para>
+ <para>For each device, we lookup the currently applied connection in NetworkManager. That implies, that the device is currently activated
+ in NetworkManager. If no such device was in NetworkManager, or if the profile has user-data <literal>org.freedesktop.nm-cloud-setup.skip=yes</literal>,
+ we skip the device. Now for each found IP address we add a static address "$ADDR/$SUBNET_PREFIX". Also we configure policy routing
+ by adding a static route "$ADDR/$SUBNET_PREFIX $GATEWAY 10, table=$TABLE" where $GATEWAY is the first IP address in the subnet and table
+ is 30400 plus the interface index. Also we add a policy routing rule "priority $TABLE from $ADDR/32 table $TABLE".</para>
+ <para>The effect is not unlike calling
+ <command>nmcli device modify "$DEVICE" ipv4.addresses "$ADDR/$SUBNET [,...]" ipv4.routes "$ADDR/32 $GATEWAY 10 table=$TABLE" ipv4.routing-rules "priority $TABLE from $ADDR/32 table $TABLE"</command>
+ for all relevant devices and all found addresses.</para>
+ </listitem>
+ </itemizedlist>
</refsect2>
</refsect1>
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index ac922c0125..5bab4bd2f2 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -364,6 +364,21 @@ again:
return b;
}
+GBytes *
+nm_g_bytes_new_from_str(const char *str)
+{
+ gsize l;
+
+ if (!str)
+ return NULL;
+
+ /* the returned array is guaranteed to have a trailing '\0'
+ * character *after* the length. */
+
+ l = strlen(str);
+ return g_bytes_new_take(nm_memdup(str, l + 1u), l);
+}
+
/**
* nm_utils_gbytes_equals:
* @bytes: (allow-none): a #GBytes array to compare. Note that
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index 5a9a4b0069..fe61459f5f 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -319,6 +319,8 @@ nm_utils_is_separator(const char c)
GBytes *nm_gbytes_get_empty(void);
+GBytes *nm_g_bytes_new_from_str(const char *str);
+
static inline gboolean
nm_gbytes_equal0(GBytes *a, GBytes *b)
{