From 3a9ec3c5a3799eccb58d268100212fe6172f00dc Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 28 Apr 2022 13:56:17 +0200 Subject: [PATCH] cloud-setup: reorder addresses to honor "primary_ip_address" The order of IPv4 addresses matters, in particular if they are in the same subnet. Kernel will mark all but the first one as "secondary". In NetworkManager's ipv4.addresses, the first address is the primary. It seems that on aliyun cloud, "private-ipv4s" URL may give the addresses in arbitrary order. The primary can be fetched from "primary-ip-address". Fix that by also fetching "primary-ip-address". Then, resort the array so that the primary is the first one in the list. https://bugzilla.redhat.com/show_bug.cgi?id=2079849 (cherry picked from commit 191baf84e215177c66aa23182fcfeae1f66dc271) --- src/nm-cloud-setup/nmcs-provider-aliyun.c | 62 ++++++++++++++++++++++- src/nm-cloud-setup/nmcs-provider.h | 12 +++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.c b/src/nm-cloud-setup/nmcs-provider-aliyun.c index 31c9830d2d..9aabca80b3 100644 --- a/src/nm-cloud-setup/nmcs-provider-aliyun.c +++ b/src/nm-cloud-setup/nmcs-provider-aliyun.c @@ -123,6 +123,7 @@ detect(NMCSProvider *provider, GTask *task) typedef enum { GET_CONFIG_FETCH_DONE_TYPE_SUBNET_VPC_CIDR_BLOCK, GET_CONFIG_FETCH_DONE_TYPE_PRIVATE_IPV4S, + GET_CONFIG_FETCH_DONE_TYPE_PRIMARY_IP_ADDRESS, GET_CONFIG_FETCH_DONE_TYPE_NETMASK, GET_CONFIG_FETCH_DONE_TYPE_GATEWAY, } GetConfigFetchDoneType; @@ -177,6 +178,16 @@ _get_config_fetch_done_cb(NMHttpClient *http_client, } break; + case GET_CONFIG_FETCH_DONE_TYPE_PRIMARY_IP_ADDRESS: + + if (nm_utils_parse_inaddr_bin(AF_INET, g_bytes_get_data(response, NULL), NULL, &tmp_addr)) { + nm_assert(config_iface_data->priv.aliyun.primary_ip_address == 0); + nm_assert(!config_iface_data->priv.aliyun.has_primary_ip_address); + config_iface_data->priv.aliyun.primary_ip_address = tmp_addr; + config_iface_data->priv.aliyun.has_primary_ip_address = TRUE; + } + break; + case GET_CONFIG_FETCH_DONE_TYPE_SUBNET_VPC_CIDR_BLOCK: if (nm_utils_parse_inaddr_prefix_bin(AF_INET, @@ -212,6 +223,26 @@ _get_config_fetch_done_cb(NMHttpClient *http_client, break; } + if (!config_iface_data->priv.aliyun.ipv4s_arr_ordered + && config_iface_data->priv.aliyun.has_primary_ip_address + && config_iface_data->ipv4s_len > 0) { + for (i = 0; i < config_iface_data->ipv4s_len; i++) { + if (config_iface_data->ipv4s_arr[i] + != config_iface_data->priv.aliyun.primary_ip_address) + continue; + if (i > 0) { + /* OK, at position [i] we found the primary address. + * Move the elements from [0..(i-1)] to [1..i] and then set [0]. */ + memmove(&config_iface_data->ipv4s_arr[1], + &config_iface_data->ipv4s_arr[0], + i * sizeof(in_addr_t)); + config_iface_data->ipv4s_arr[0] = config_iface_data->priv.aliyun.primary_ip_address; + } + break; + } + config_iface_data->priv.aliyun.ipv4s_arr_ordered = TRUE; + } + out: get_config_data->n_pending--; _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error)); @@ -235,6 +266,17 @@ _get_config_fetch_done_cb_private_ipv4s(GObject *source, GAsyncResult *result, g GET_CONFIG_FETCH_DONE_TYPE_PRIVATE_IPV4S); } +static void +_get_config_fetch_done_cb_primary_ip_address(GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + _get_config_fetch_done_cb(NM_HTTP_CLIENT(source), + result, + user_data, + GET_CONFIG_FETCH_DONE_TYPE_PRIMARY_IP_ADDRESS); +} + static void _get_config_fetch_done_cb_netmask(GObject *source, GAsyncResult *result, gpointer user_data) { @@ -297,6 +339,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us gs_free char *uri2 = NULL; gs_free char *uri3 = NULL; gs_free char *uri4 = NULL; + gs_free char *uri5 = NULL; config_iface_data = g_hash_table_lookup(get_config_data->result_dict, v_hwaddr); @@ -361,6 +404,23 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us nm_http_client_poll_get( http_client, (uri3 = _aliyun_uri_interfaces(v_mac_data->path, + NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", + "primary-ip-address")), + HTTP_TIMEOUT_MS, + 512 * 1024, + 10000, + 1000, + NULL, + get_config_data->intern_cancellable, + NULL, + NULL, + _get_config_fetch_done_cb_primary_ip_address, + nm_utils_user_data_pack(get_config_data, config_iface_data)); + + get_config_data->n_pending++; + nm_http_client_poll_get( + http_client, + (uri4 = _aliyun_uri_interfaces(v_mac_data->path, NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", "netmask")), HTTP_TIMEOUT_MS, @@ -377,7 +437,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us get_config_data->n_pending++; nm_http_client_poll_get( http_client, - (uri4 = _aliyun_uri_interfaces(v_mac_data->path, + (uri5 = _aliyun_uri_interfaces(v_mac_data->path, NM_STR_HAS_SUFFIX(v_mac_data->path, "/") ? "" : "/", "gateway")), HTTP_TIMEOUT_MS, diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h index bce41dcb82..8ec240e5b9 100644 --- a/src/nm-cloud-setup/nmcs-provider.h +++ b/src/nm-cloud-setup/nmcs-provider.h @@ -36,6 +36,18 @@ typedef struct { * nmcs_provider_get_config(). */ bool was_requested : 1; + /* Usually we would want that the parent class NMCSProvider is not aware about + * the implementations. However, it's convenient to track implementation specific data + * here, thus we violate such separation. In practice, all subclasses are known + * at compile time, and it will be simpler this way. */ + struct { + struct { + in_addr_t primary_ip_address; + bool has_primary_ip_address : 1; + bool ipv4s_arr_ordered : 1; + } aliyun; + } priv; + } NMCSProviderGetConfigIfaceData; static inline gboolean