diff --git a/man/nm-cloud-setup.xml b/man/nm-cloud-setup.xml index 7493cc1d7f..976fc64724 100644 --- a/man/nm-cloud-setup.xml +++ b/man/nm-cloud-setup.xml @@ -256,7 +256,9 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst Also, if the device is currently not activated in NetworkManager or if the currently activated profile has a user-data org.freedesktop.nm-cloud-setup.skip=yes, it is skipped. - Then, the tool will change the runtime configuration of the device. + If only one interface and one address is configured, then the tool does nothing + and leaves the automatic configuration that was obtained via DHCP. + Otherwise, the tool will change the runtime configuration of the device. Add static IPv4 addresses for all the configured addresses from local-ipv4s with @@ -267,15 +269,25 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst Choose a route table 30400 + the index of the interface and add a default route 0.0.0.0/0. The gateway is the first IP address in the CIDR subnet block. For - example, we might get a route "0.0.0.0/0 172.16.5.1 10 table=30401". + example, we might get a route "0.0.0.0/0 172.16.5.1 10 table=30400". + Also choose a route table 30200 + the interface index. This + contains a direct routes to the subnets of this interface. Finally, add a policy routing rule for each address. For example - "priority 30401 from 172.16.5.3/32 table 30401, priority 30401 from 172.16.5.4/32 table 30401". + "priority 30200 from 172.16.5.3/32 table 30200, priority 30200 from 172.16.5.4/32 table 30200". + and + "priority 30400 from 172.16.5.3/32 table 30400, priority 30400 from 172.16.5.4/32 table 30400" + The 30200+ rules select the table to reach the subnet directly, while the 30400+ rules use the + default route. Also add a rule + "priority 30350 table main suppress_prefixlength 0". This has a priority between + the two previous rules and causes a lookup of routes in the main table while ignoring the default + route. The purpose of this is so that other specific routes in the main table are honored over + the default route in table 30400+. With above example, this roughly corresponds for interface eth0 to - 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". + nmcli device modify "eth0" ipv4.addresses "172.16.5.3/24,172.16.5.4/24" ipv4.routes "172.16.5.0/24 0.0.0.0 10 table=30200, 0.0.0.0/0 172.16.5.1 10 table=30400" ipv4.routing-rules "priority 30200 from 172.16.5.3/32 table 30200, priority 30200 from 172.16.5.4/32 table 30200, priority 20350 table main suppress_prefixlength 0, priority 30400 from 172.16.5.3/32 table 30400, priority 30400 from 172.16.5.4/32 table 30400". 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 on disk is not affected. @@ -360,14 +372,8 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst At this point, we have a list of all interfaces (by MAC address) and their configured IPv4 addresses. - 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 org.freedesktop.nm-cloud-setup.skip=yes, - 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". - The effect is not unlike calling - 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" - for all relevant devices and all found addresses. + Then the tool configures the system like doing for AWS environment. That is, using source based policy routing + with the tables/rules 30200/30400. @@ -389,9 +395,10 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst of available interface. Interfaces are identified by their MAC address. - Then for each interface fetch http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/vpc-cidr-block - , http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/private-ipv4s and - http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/netmask. + Then for each interface fetch http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/vpc-cidr-block, + http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/private-ipv4s, + http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/netmask and + http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/gateway. Thereby we get a list of private IPv4 addresses, one CIDR subnet block and private IPv4 addresses prefix. @@ -399,31 +406,10 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst If no ethernet device for the respective MAC address is found, it is skipped. Also, if the device is currently not activated in NetworkManager or if the currently activated profile has a user-data org.freedesktop.nm-cloud-setup.skip=yes, - it is skipped. - Then, the tool will change the runtime configuration of the device. - - - Add static IPv4 addresses for all the configured addresses from private-ipv4s with - prefix length according to netmask. For example, - we might have here 2 IP addresses like "10.0.0.150/24,10.0.0.152/24". - - - Choose a route table 30400 + the index of the interface and - add a default route 0.0.0.0/0. The gateway - is the default gateway retrieved from metadata server. For - example, we might get a route "0.0.0.0/0 10.0.0.253 10 table=30400". - - - Finally, add a policy routing rule for each address. For example - "priority 30400 from 10.0.0.150/32 table 30400, priority 30400 from 10.0.0.152/32 table 30400". - - - With above example, this roughly corresponds for interface eth0 to - nmcli device modify "eth0" ipv4.addresses "10.0.0.150/24,10.0.0.152/24" ipv4.routes "0.0.0.0/0 10.0.0.253 10 table=30400" ipv4.routing-rules "priority 30400 from 10.0.0.150/32 table 30400, priority 30400 from 10.0.0.152/32 table 30400". - 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 on disk is not affected. - + it is skipped. Also, there is only one interface and one IP address, the tool does nothing. + Then the tool configures the system like doing for AWS environment. That is, using source based policy routing + with the tables/rules 30200/30400. One difference to AWS is that the gateway is also fetched via metadata instead + of using the first IP address in the subnet. diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c index 8dc6785179..916f41da91 100644 --- a/src/nm-cloud-setup/main.c +++ b/src/nm-cloud-setup/main.c @@ -4,6 +4,8 @@ #include "libnm-client-aux-extern/nm-libnm-aux.h" +#include + #include "nm-cloud-setup-utils.h" #include "nmcs-provider-ec2.h" #include "nmcs-provider-gcp.h" @@ -200,30 +202,30 @@ _nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr) /*****************************************************************************/ typedef struct { - GMainLoop * main_loop; - GHashTable *config_dict; + GMainLoop * main_loop; + NMCSProviderGetConfigResult *result; } GetConfigData; static void -_get_config_cb(GObject *source, GAsyncResult *result, gpointer user_data) +_get_config_cb(GObject *source, GAsyncResult *res, gpointer user_data) { - GetConfigData * data = user_data; - gs_unref_hashtable GHashTable *config_dict = NULL; - gs_free_error GError *error = NULL; + GetConfigData * data = user_data; + nm_auto_free_nmcs_provider_get_config_result NMCSProviderGetConfigResult *result = NULL; + gs_free_error GError *error = NULL; - config_dict = nmcs_provider_get_config_finish(NMCS_PROVIDER(source), result, &error); + result = nmcs_provider_get_config_finish(NMCS_PROVIDER(source), res, &error); - if (!config_dict) { + if (!result) { if (!nm_utils_error_is_cancelled(error)) _LOGI("failure to get meta data: %s", error->message); } else _LOGD("meta data received"); - data->config_dict = g_steal_pointer(&config_dict); + data->result = g_steal_pointer(&result); g_main_loop_quit(data->main_loop); } -static GHashTable * +static NMCSProviderGetConfigResult * _get_config(GCancellable *sigterm_cancellable, NMCSProvider *provider, NMClient *nmc) { nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new(NULL, FALSE); @@ -243,7 +245,7 @@ _get_config(GCancellable *sigterm_cancellable, NMCSProvider *provider, NMClient g_main_loop_run(main_loop); - return data.config_dict; + return data.result; } /*****************************************************************************/ @@ -269,7 +271,9 @@ _nmc_skip_connection(NMConnection *connection) static gboolean _nmc_mangle_connection(NMDevice * device, NMConnection * connection, + const NMCSProviderGetConfigResult * result, const NMCSProviderGetConfigIfaceData *config_data, + gboolean * out_skipped_single_addr, gboolean * out_changed) { NMSettingIPConfig * s_ip; @@ -277,10 +281,6 @@ _nmc_mangle_connection(NMDevice * device, NMConnection * remote_connection; NMSettingIPConfig * remote_s_ip = NULL; gsize i; - in_addr_t gateway; - gint64 rt_metric; - guint32 rt_table; - NMIPRoute * route_entry; gboolean addrs_changed = FALSE; gboolean rules_changed = FALSE; gboolean routes_changed = FALSE; @@ -288,6 +288,9 @@ _nmc_mangle_connection(NMDevice * device, gs_unref_ptrarray GPtrArray *rules_new = NULL; gs_unref_ptrarray GPtrArray *routes_new = NULL; + NM_SET_OUT(out_skipped_single_addr, FALSE); + NM_SET_OUT(out_changed, FALSE); + if (!nm_streq0(nm_connection_get_connection_type(connection), NM_SETTING_WIRED_SETTING_NAME)) return FALSE; @@ -329,48 +332,84 @@ _nmc_mangle_connection(NMDevice * device, } } - if (config_data->has_ipv4s && config_data->has_cidr) { - for (i = 0; i < config_data->ipv4s_len; i++) { - NMIPAddress *entry; + if (result->num_valid_ifaces <= 1 && result->num_ipv4s <= 1) { + /* this setup only has one interface and one IPv4 address (or less). + * We don't need to configure policy routing in this case. */ + NM_SET_OUT(out_skipped_single_addr, TRUE); + } else if (config_data->has_ipv4s && config_data->has_cidr) { + gs_unref_hashtable GHashTable *unique_subnets = + g_hash_table_new(nm_direct_hash, g_direct_equal); + NMIPAddress * addr_entry; + NMIPRoute * route_entry; + NMIPRoutingRule *rule_entry; + in_addr_t gateway; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - entry = nm_ip_address_new_binary(AF_INET, - &config_data->ipv4s_arr[i], - config_data->cidr_prefix, - NULL); - if (entry) - g_ptr_array_add(addrs_new, entry); + for (i = 0; i < config_data->ipv4s_len; i++) { + addr_entry = nm_ip_address_new_binary(AF_INET, + &config_data->ipv4s_arr[i], + config_data->cidr_prefix, + NULL); + nm_assert(addr_entry); + g_ptr_array_add(addrs_new, addr_entry); } + if (config_data->has_gateway && config_data->gateway) { gateway = config_data->gateway; } else { gateway = nm_utils_ip4_address_clear_host_address(config_data->cidr_addr, config_data->cidr_prefix); - ((guint8 *) &gateway)[3] += 1; + if (config_data->cidr_prefix < 32) + ((guint8 *) &gateway)[3] += 1; } - rt_metric = 10; - rt_table = 30400 + config_data->iface_idx; - route_entry = - nm_ip_route_new_binary(AF_INET, &nm_ip_addr_zero, 0, &gateway, rt_metric, NULL); + for (i = 0; i < config_data->ipv4s_len; i++) { + in_addr_t a = config_data->ipv4s_arr[i]; + + a = nm_utils_ip4_address_clear_host_address(a, config_data->cidr_prefix); + + G_STATIC_ASSERT_EXPR(sizeof(gsize) >= sizeof(in_addr_t)); + if (g_hash_table_add(unique_subnets, GSIZE_TO_POINTER(a))) { + route_entry = + nm_ip_route_new_binary(AF_INET, &a, config_data->cidr_prefix, NULL, 10, NULL); + nm_ip_route_set_attribute(route_entry, + NM_IP_ROUTE_ATTRIBUTE_TABLE, + g_variant_new_uint32(30200 + config_data->iface_idx)); + g_ptr_array_add(routes_new, route_entry); + } + + rule_entry = nm_ip_routing_rule_new(AF_INET); + nm_ip_routing_rule_set_priority(rule_entry, 30200 + config_data->iface_idx); + nm_ip_routing_rule_set_from(rule_entry, + _nm_utils_inet4_ntop(config_data->ipv4s_arr[i], sbuf), + 32); + nm_ip_routing_rule_set_table(rule_entry, 30200 + config_data->iface_idx); + nm_assert(nm_ip_routing_rule_validate(rule_entry, NULL)); + g_ptr_array_add(rules_new, rule_entry); + } + + rule_entry = nm_ip_routing_rule_new(AF_INET); + nm_ip_routing_rule_set_priority(rule_entry, 30350); + nm_ip_routing_rule_set_table(rule_entry, RT_TABLE_MAIN); + nm_ip_routing_rule_set_suppress_prefixlength(rule_entry, 0); + nm_assert(nm_ip_routing_rule_validate(rule_entry, NULL)); + g_ptr_array_add(rules_new, rule_entry); + + route_entry = nm_ip_route_new_binary(AF_INET, &nm_ip_addr_zero, 0, &gateway, 10, NULL); nm_ip_route_set_attribute(route_entry, NM_IP_ROUTE_ATTRIBUTE_TABLE, - g_variant_new_uint32(rt_table)); + g_variant_new_uint32(30400 + config_data->iface_idx)); g_ptr_array_add(routes_new, route_entry); for (i = 0; i < config_data->ipv4s_len; i++) { - NMIPRoutingRule *entry; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - entry = nm_ip_routing_rule_new(AF_INET); - nm_ip_routing_rule_set_priority(entry, rt_table); - nm_ip_routing_rule_set_from(entry, + rule_entry = nm_ip_routing_rule_new(AF_INET); + nm_ip_routing_rule_set_priority(rule_entry, 30400 + config_data->iface_idx); + nm_ip_routing_rule_set_from(rule_entry, _nm_utils_inet4_ntop(config_data->ipv4s_arr[i], sbuf), 32); - nm_ip_routing_rule_set_table(entry, rt_table); - - nm_assert(nm_ip_routing_rule_validate(entry, NULL)); - - g_ptr_array_add(rules_new, entry); + nm_ip_routing_rule_set_table(rule_entry, 30400 + config_data->iface_idx); + nm_assert(nm_ip_routing_rule_validate(rule_entry, NULL)); + g_ptr_array_add(rules_new, rule_entry); } } @@ -395,34 +434,20 @@ _nmc_mangle_connection(NMDevice * device, /*****************************************************************************/ -static guint -_config_data_get_num_valid(GHashTable *config_dict) -{ - const NMCSProviderGetConfigIfaceData *config_data; - GHashTableIter h_iter; - guint n = 0; - - g_hash_table_iter_init(&h_iter, config_dict); - while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &config_data)) { - if (nmcs_provider_get_config_iface_data_is_valid(config_data)) - n++; - } - - return n; -} - static gboolean -_config_one(GCancellable * sigterm_cancellable, - NMClient * nmc, - gboolean is_single_nic, - const char * hwaddr, - const NMCSProviderGetConfigIfaceData *config_data) +_config_one(GCancellable * sigterm_cancellable, + NMClient * nmc, + const NMCSProviderGetConfigResult *result, + guint idx) { - gs_unref_object NMDevice *device = NULL; - gs_unref_object NMConnection *applied_connection = NULL; + const NMCSProviderGetConfigIfaceData *config_data = result->iface_datas_arr[idx]; + const char * hwaddr = config_data->hwaddr; + gs_unref_object NMDevice *device = NULL; + gs_unref_object NMConnection *applied_connection = NULL; guint64 applied_version_id; gs_free_error GError *error = NULL; gboolean changed; + gboolean skipped_single_addr; gboolean version_id_changed; guint try_count; gboolean any_changes = FALSE; @@ -443,6 +468,14 @@ _config_one(GCancellable * sigterm_cancellable, return FALSE; } + if (config_data->iface_idx >= 100) { + /* since we use the iface_idx to select a table number, the range is limited from + * 0 to 99. Note that the providers are required to provide increasing numbers, + * so this means we bail out after the first 100 devices. */ + _LOGD("config device %s: skip because number of supported interfaces reached", hwaddr); + return FALSE; + } + _LOGD("config device %s: configuring \"%s\" (%s)...", hwaddr, nm_device_get_iface(device) ?: "/unknown/", @@ -471,16 +504,30 @@ try_again: return any_changes; } - if (!_nmc_mangle_connection(device, applied_connection, config_data, &changed)) { + if (!_nmc_mangle_connection(device, + applied_connection, + result, + config_data, + &skipped_single_addr, + &changed)) { _LOGD("config device %s: device has no suitable applied connection. Skip", hwaddr); return any_changes; } if (!changed) { - _LOGD("config device %s: device needs no update to applied connection \"%s\" (%s). Skip", - hwaddr, - nm_connection_get_id(applied_connection), - nm_connection_get_uuid(applied_connection)); + if (skipped_single_addr) { + _LOGD("config device %s: device needs no update to applied connection \"%s\" (%s) " + "because there are not multiple IP addresses. Skip", + hwaddr, + nm_connection_get_id(applied_connection), + nm_connection_get_uuid(applied_connection)); + } else { + _LOGD( + "config device %s: device needs no update to applied connection \"%s\" (%s). Skip", + hwaddr, + nm_connection_get_id(applied_connection), + nm_connection_get_uuid(applied_connection)); + } return any_changes; } @@ -489,7 +536,7 @@ try_again: nm_connection_get_id(applied_connection), nm_connection_get_uuid(applied_connection)); - /* we are about to call Reapply(). If if that fails, it counts as if we changed something. */ + /* we are about to call Reapply(). Even if that fails, it counts as if we changed something. */ any_changes = TRUE; if (!nmcs_device_reapply(device, @@ -525,19 +572,15 @@ try_again: } static gboolean -_config_all(GCancellable *sigterm_cancellable, NMClient *nmc, GHashTable *config_dict) +_config_all(GCancellable * sigterm_cancellable, + NMClient * nmc, + const NMCSProviderGetConfigResult *result) { - GHashTableIter h_iter; - const NMCSProviderGetConfigIfaceData *c_config_data; - const char * c_hwaddr; - gboolean is_single_nic; - gboolean any_changes = FALSE; + gboolean any_changes = FALSE; + guint i; - is_single_nic = (_config_data_get_num_valid(config_dict) <= 1); - - g_hash_table_iter_init(&h_iter, config_dict); - while (g_hash_table_iter_next(&h_iter, (gpointer *) &c_hwaddr, (gpointer *) &c_config_data)) { - if (_config_one(sigterm_cancellable, nmc, is_single_nic, c_hwaddr, c_config_data)) + for (i = 0; i < result->n_iface_datas; i++) { + if (_config_one(sigterm_cancellable, nmc, result, i)) any_changes = TRUE; } @@ -564,12 +607,12 @@ sigterm_handler(gpointer user_data) int main(int argc, const char *const *argv) { - gs_unref_object GCancellable * sigterm_cancellable = NULL; - nm_auto_destroy_and_unref_gsource GSource *sigterm_source = NULL; - gs_unref_object NMCSProvider *provider = NULL; - gs_unref_object NMClient *nmc = NULL; - gs_unref_hashtable GHashTable *config_dict = NULL; - gs_free_error GError *error = NULL; + gs_unref_object GCancellable * sigterm_cancellable = NULL; + nm_auto_destroy_and_unref_gsource GSource *sigterm_source = NULL; + gs_unref_object NMCSProvider *provider = NULL; + gs_unref_object NMClient * nmc = NULL; + nm_auto_free_nmcs_provider_get_config_result NMCSProviderGetConfigResult *result = NULL; + gs_free_error GError *error = NULL; _nm_logging_enabled_init(g_getenv(NMCS_ENV_VARIABLE("NM_CLOUD_SETUP_LOG"))); @@ -614,17 +657,17 @@ main(int argc, const char *const *argv) goto done; } - config_dict = _get_config(sigterm_cancellable, provider, nmc); - if (!config_dict) + result = _get_config(sigterm_cancellable, provider, nmc); + if (!result) goto done; - if (_config_all(sigterm_cancellable, nmc, config_dict)) + if (_config_all(sigterm_cancellable, nmc, result)) _LOGI("some changes were applied for provider %s", nmcs_provider_get_name(provider)); else _LOGD("no changes were applied for provider %s", nmcs_provider_get_name(provider)); done: - nm_clear_pointer(&config_dict, g_hash_table_unref); + nm_clear_pointer(&result, nmcs_provider_get_config_result_free); g_clear_object(&nmc); g_clear_object(&provider); diff --git a/src/nm-cloud-setup/nmcs-provider-aliyun.c b/src/nm-cloud-setup/nmcs-provider-aliyun.c index 01a4af0ff5..126980fdfe 100644 --- a/src/nm-cloud-setup/nmcs-provider-aliyun.c +++ b/src/nm-cloud-setup/nmcs-provider-aliyun.c @@ -134,7 +134,6 @@ _get_config_fetch_done_cb(NMHttpClient * http_client, GetConfigFetchDoneType fetch_type) { NMCSProviderGetConfigTaskData *get_config_data; - const char * hwaddr = NULL; gs_unref_bytes GBytes *response = NULL; gs_free_error GError * error = NULL; NMCSProviderGetConfigIfaceData *config_iface_data; @@ -146,7 +145,7 @@ _get_config_fetch_done_cb(NMHttpClient * http_client, gsize i; gsize len; - nm_utils_user_data_unpack(user_data, &get_config_data, &hwaddr); + nm_utils_user_data_unpack(user_data, &get_config_data, &config_iface_data); nm_http_client_poll_get_finish(http_client, result, NULL, &response, &error); @@ -156,8 +155,6 @@ _get_config_fetch_done_cb(NMHttpClient * http_client, if (error) goto out; - config_iface_data = g_hash_table_lookup(get_config_data->result_dict, hwaddr); - switch (fetch_type) { case GET_CONFIG_FETCH_DONE_TYPE_PRIVATE_IPV4S: @@ -300,22 +297,21 @@ _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; - const char * hwaddr; - if (!g_hash_table_lookup_extended(get_config_data->result_dict, - v_hwaddr, - (gpointer *) &hwaddr, - (gpointer *) &config_iface_data)) { + config_iface_data = g_hash_table_lookup(get_config_data->result_dict, v_hwaddr); + + if (!config_iface_data) { if (!get_config_data->any) { _LOGD("get-config: skip fetching meta data for %s (%s)", v_hwaddr, v_mac_data->path); continue; } - config_iface_data = nmcs_provider_get_config_iface_data_new(FALSE); - g_hash_table_insert(get_config_data->result_dict, - (char *) (hwaddr = g_strdup(v_hwaddr)), - config_iface_data); + + config_iface_data = + nmcs_provider_get_config_iface_data_create(get_config_data->result_dict, + FALSE, + v_hwaddr); } nm_assert(config_iface_data->iface_idx == -1); @@ -324,7 +320,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us _LOGD("get-config: start fetching meta data for #%" G_GSSIZE_FORMAT ", %s (%s)", config_iface_data->iface_idx, - hwaddr, + config_iface_data->hwaddr, v_mac_data->path); get_config_data->n_pending++; @@ -342,7 +338,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NULL, NULL, _get_config_fetch_done_cb_vpc_cidr_block, - nm_utils_user_data_pack(get_config_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, config_iface_data)); get_config_data->n_pending++; nm_http_client_poll_get( @@ -359,7 +355,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NULL, NULL, _get_config_fetch_done_cb_private_ipv4s, - nm_utils_user_data_pack(get_config_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, config_iface_data)); get_config_data->n_pending++; nm_http_client_poll_get( @@ -376,7 +372,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NULL, NULL, _get_config_fetch_done_cb_netmask, - nm_utils_user_data_pack(get_config_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, config_iface_data)); get_config_data->n_pending++; nm_http_client_poll_get( @@ -393,7 +389,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NULL, NULL, _get_config_fetch_done_cb_gateway, - nm_utils_user_data_pack(get_config_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, config_iface_data)); } _nmcs_provider_get_config_task_maybe_return(get_config_data, NULL); diff --git a/src/nm-cloud-setup/nmcs-provider-azure.c b/src/nm-cloud-setup/nmcs-provider-azure.c index 69785d64a8..b3f0c68ba6 100644 --- a/src/nm-cloud-setup/nmcs-provider-azure.c +++ b/src/nm-cloud-setup/nmcs-provider-azure.c @@ -97,7 +97,6 @@ typedef struct { gssize intern_iface_idx; gssize extern_iface_idx; guint n_iface_data_pending; - const char * hwaddr; } AzureIfaceData; static void @@ -378,25 +377,24 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data) goto out_done; } - if (!g_hash_table_lookup_extended(get_config_data->result_dict, - v_hwaddr, - (gpointer *) &iface_data->hwaddr, - (gpointer *) &iface_data->iface_get_config)) { + iface_data->iface_get_config = g_hash_table_lookup(get_config_data->result_dict, v_hwaddr); + + if (!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 *) (iface_data->hwaddr = g_steal_pointer(&v_hwaddr)), - iface_data->iface_get_config); + iface_data->iface_get_config = + nmcs_provider_get_config_iface_data_create(get_config_data->result_dict, + FALSE, + v_hwaddr); } 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); + iface_data->iface_get_config->hwaddr); error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "duplicate MAC address for index %" G_GSSIZE_FORMAT, iface_data->intern_iface_idx); @@ -408,7 +406,7 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data) _LOGD("interface[%" G_GSSIZE_FORMAT "]: found a matching device with hwaddr %s", iface_data->intern_iface_idx, - iface_data->hwaddr); + iface_data->iface_get_config->hwaddr); nm_sprintf_buf(buf, "%" G_GSSIZE_FORMAT "/ipv4/ipAddress/", iface_data->intern_iface_idx); @@ -488,7 +486,6 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat .intern_iface_idx = intern_iface_idx, .extern_iface_idx = extern_iface_idx_cnt++, .n_iface_data_pending = 0, - .hwaddr = NULL, }; g_ptr_array_add(ifaces_arr, iface_data); } diff --git a/src/nm-cloud-setup/nmcs-provider-ec2.c b/src/nm-cloud-setup/nmcs-provider-ec2.c index 6f83238d68..9fe625182c 100644 --- a/src/nm-cloud-setup/nmcs-provider-ec2.c +++ b/src/nm-cloud-setup/nmcs-provider-ec2.c @@ -122,14 +122,13 @@ _get_config_fetch_done_cb(NMHttpClient *http_client, gboolean is_local_ipv4) { 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; - nm_utils_user_data_unpack(user_data, &get_config_data, &hwaddr); + nm_utils_user_data_unpack(user_data, &get_config_data, &config_iface_data); nm_http_client_poll_get_finish(http_client, result, NULL, &response, &error); @@ -139,8 +138,6 @@ _get_config_fetch_done_cb(NMHttpClient *http_client, if (error) goto out; - config_iface_data = g_hash_table_lookup(get_config_data->result_dict, hwaddr); - if (is_local_ipv4) { gs_free const char **s_addrs = NULL; gsize i, len; @@ -236,22 +233,20 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NMCSProviderGetConfigIfaceData *config_iface_data; gs_free char * uri1 = NULL; gs_free char * uri2 = NULL; - const char * hwaddr; - if (!g_hash_table_lookup_extended(get_config_data->result_dict, - v_hwaddr, - (gpointer *) &hwaddr, - (gpointer *) &config_iface_data)) { + config_iface_data = g_hash_table_lookup(get_config_data->result_dict, v_hwaddr); + + if (!config_iface_data) { if (!get_config_data->any) { _LOGD("get-config: skip fetching meta data for %s (%s)", v_hwaddr, v_mac_data->path); continue; } - config_iface_data = nmcs_provider_get_config_iface_data_new(FALSE); - g_hash_table_insert(get_config_data->result_dict, - (char *) (hwaddr = g_strdup(v_hwaddr)), - config_iface_data); + config_iface_data = + nmcs_provider_get_config_iface_data_create(get_config_data->result_dict, + FALSE, + v_hwaddr); } nm_assert(config_iface_data->iface_idx == -1); @@ -260,7 +255,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us _LOGD("get-config: start fetching meta data for #%" G_GSSIZE_FORMAT ", %s (%s)", config_iface_data->iface_idx, - hwaddr, + config_iface_data->hwaddr, v_mac_data->path); get_config_data->n_pending++; @@ -278,7 +273,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NULL, NULL, _get_config_fetch_done_cb_subnet_ipv4_cidr_block, - nm_utils_user_data_pack(get_config_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, config_iface_data)); get_config_data->n_pending++; nm_http_client_poll_get( @@ -295,7 +290,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us NULL, NULL, _get_config_fetch_done_cb_local_ipv4s, - nm_utils_user_data_pack(get_config_data, hwaddr)); + nm_utils_user_data_pack(get_config_data, config_iface_data)); } _nmcs_provider_get_config_task_maybe_return(get_config_data, NULL); diff --git a/src/nm-cloud-setup/nmcs-provider-gcp.c b/src/nm-cloud-setup/nmcs-provider-gcp.c index eacfd5e248..60425ad978 100644 --- a/src/nm-cloud-setup/nmcs-provider-gcp.c +++ b/src/nm-cloud-setup/nmcs-provider-gcp.c @@ -247,7 +247,6 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data) 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; @@ -273,26 +272,25 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data) goto out_done; } - if (!g_hash_table_lookup_extended(get_config_data->result_dict, - v_hwaddr, - (gpointer *) &hwaddr, - (gpointer *) &iface_data->iface_get_config)) { + iface_data->iface_get_config = g_hash_table_lookup(get_config_data->result_dict, v_hwaddr); + + if (!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); + iface_data->iface_get_config = + nmcs_provider_get_config_iface_data_create(get_config_data->result_dict, + FALSE, + v_hwaddr); 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); + iface_data->iface_get_config->hwaddr); error = nm_utils_error_new(NM_UTILS_ERROR_UNKNOWN, "duplicate MAC address for index %" G_GSSIZE_FORMAT, iface_data->intern_iface_idx); @@ -306,7 +304,7 @@ _get_config_iface_cb(GObject *source, GAsyncResult *result, gpointer user_data) _LOGI("GCP interface[%" G_GSSIZE_FORMAT "]: found a %sdevice with hwaddr %s", iface_data->intern_iface_idx, is_requested ? "requested " : "", - hwaddr); + iface_data->iface_get_config->hwaddr); nm_sprintf_buf(sbuf, "%" G_GSSIZE_FORMAT "/forwarded-ips/", iface_data->intern_iface_idx); diff --git a/src/nm-cloud-setup/nmcs-provider.c b/src/nm-cloud-setup/nmcs-provider.c index 678152aa95..138e78d41b 100644 --- a/src/nm-cloud-setup/nmcs-provider.c +++ b/src/nm-cloud-setup/nmcs-provider.c @@ -49,6 +49,90 @@ nmcs_provider_get_main_context(NMCSProvider *self) return nm_http_client_get_main_context(NMCS_PROVIDER_GET_PRIVATE(self)->http_client); } +/*****************************************************************************/ + +static int +_result_new_sort_iface_data(gconstpointer pa, gconstpointer pb) +{ + const NMCSProviderGetConfigIfaceData *a = *((const NMCSProviderGetConfigIfaceData *const *) pa); + const NMCSProviderGetConfigIfaceData *b = *((const NMCSProviderGetConfigIfaceData *const *) pb); + + /* negative iface_idx are sorted to the end. */ + NM_CMP_DIRECT((a->iface_idx < 0), (b->iface_idx < 0)); + + NM_CMP_FIELD(a, b, iface_idx); + return 0; +} + +static NMCSProviderGetConfigResult * +nmcs_provider_get_config_result_new(GHashTable *iface_datas) +{ + const NMCSProviderGetConfigIfaceData *iface_data; + NMCSProviderGetConfigResult * result; + GHashTableIter h_iter; + guint num_valid_ifaces = 0; + guint num_ipv4s = 0; + GPtrArray * ptrarr; + guint n_iface_datas; + + n_iface_datas = g_hash_table_size(iface_datas); + + ptrarr = g_ptr_array_sized_new(n_iface_datas + 1u); + + g_hash_table_iter_init(&h_iter, iface_datas); + while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &iface_data)) { + if (nmcs_provider_get_config_iface_data_is_valid(iface_data)) { + num_valid_ifaces++; + num_ipv4s += iface_data->ipv4s_len; + } + g_ptr_array_add(ptrarr, (gpointer) iface_data); + } + + g_ptr_array_sort(ptrarr, _result_new_sort_iface_data); + + nm_assert(n_iface_datas == ptrarr->len); + + g_ptr_array_add(ptrarr, NULL); + + result = g_new(NMCSProviderGetConfigResult, 1); + *result = (NMCSProviderGetConfigResult){ + .iface_datas = g_hash_table_ref(iface_datas), + .n_iface_datas = n_iface_datas, + .iface_datas_arr = + (const NMCSProviderGetConfigIfaceData **) g_ptr_array_free(ptrarr, FALSE), + .num_valid_ifaces = num_valid_ifaces, + .num_ipv4s = num_ipv4s, + }; + +#if NM_MORE_ASSERTS > 5 + { + gsize iface_idx_expected = 0; + guint i; + + for (i = 0; i < result->n_iface_datas; i++) { + if (result->iface_datas_arr[i]->iface_idx < 0) { + nm_assert(result->iface_datas_arr[i]->iface_idx == -1); + iface_idx_expected = -1; + continue; + } + nm_assert(result->iface_datas_arr[i]->iface_idx == iface_idx_expected); + iface_idx_expected++; + } + } +#endif + + return result; +} + +void +nmcs_provider_get_config_result_free(NMCSProviderGetConfigResult *result) +{ + if (result) { + nm_g_hash_table_unref(result->iface_datas); + g_free((gpointer) result->iface_datas_arr); + g_free(result); + } +} /*****************************************************************************/ @@ -90,15 +174,25 @@ nmcs_provider_detect_finish(NMCSProvider *self, GAsyncResult *result, GError **e /*****************************************************************************/ NMCSProviderGetConfigIfaceData * -nmcs_provider_get_config_iface_data_new(gboolean was_requested) +nmcs_provider_get_config_iface_data_create(GHashTable *iface_datas, + gboolean was_requested, + const char *hwaddr) { NMCSProviderGetConfigIfaceData *iface_data; + nm_assert(hwaddr); + iface_data = g_slice_new(NMCSProviderGetConfigIfaceData); *iface_data = (NMCSProviderGetConfigIfaceData){ + .hwaddr = g_strdup(hwaddr), .iface_idx = -1, .was_requested = was_requested, }; + + /* the has does not own the key (iface_datta->hwaddr), the lifetime of the + * key is associated with the iface_data instance. */ + g_hash_table_replace(iface_datas, (char *) iface_data->hwaddr, iface_data); + return iface_data; } @@ -109,6 +203,7 @@ _iface_data_free(gpointer data) g_free(iface_data->ipv4s_arr); g_free(iface_data->iproutes_arr); + g_free((char *) iface_data->hwaddr); nm_g_slice_free(iface_data); } @@ -137,8 +232,8 @@ _get_config_task_maybe_return(NMCSProviderGetConfigTaskData *get_config_data, GE } 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); + nmcs_provider_get_config_result_new(get_config_data->result_dict), + (GDestroyNotify) nmcs_provider_get_config_result_free); } nm_clear_g_signal_handler(g_task_get_cancellable(get_config_data->task), @@ -187,16 +282,13 @@ nmcs_provider_get_config(NMCSProvider * self, *get_config_data = (NMCSProviderGetConfigTaskData){ .task = nm_g_task_new(self, cancellable, nmcs_provider_get_config, callback, user_data), .any = any, - .result_dict = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, _iface_data_free), + .result_dict = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, _iface_data_free), }; nmcs_wait_for_objects_register(get_config_data->task); - for (; hwaddrs && hwaddrs[0]; hwaddrs++) { - g_hash_table_insert(get_config_data->result_dict, - g_strdup(hwaddrs[0]), - nmcs_provider_get_config_iface_data_new(TRUE)); - } + for (; hwaddrs && hwaddrs[0]; hwaddrs++) + nmcs_provider_get_config_iface_data_create(get_config_data->result_dict, TRUE, hwaddrs[0]); if (cancellable) { gulong cancelled_id; @@ -217,7 +309,7 @@ nmcs_provider_get_config(NMCSProvider * self, NMCS_PROVIDER_GET_CLASS(self)->get_config(self, get_config_data); } -GHashTable * +NMCSProviderGetConfigResult * nmcs_provider_get_config_finish(NMCSProvider *self, GAsyncResult *result, GError **error) { g_return_val_if_fail(NMCS_IS_PROVIDER(self), FALSE); diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h index 13212b8e4c..3663205964 100644 --- a/src/nm-cloud-setup/nmcs-provider.h +++ b/src/nm-cloud-setup/nmcs-provider.h @@ -10,6 +10,10 @@ /*****************************************************************************/ typedef struct { + /* And it's exactly the same pointer that is also the key for the iface_datas + * dictionary. */ + const char *hwaddr; + in_addr_t *ipv4s_arr; gsize ipv4s_len; @@ -41,7 +45,43 @@ nmcs_provider_get_config_iface_data_is_valid(const NMCSProviderGetConfigIfaceDat && ((config_data->has_ipv4s && config_data->has_cidr) || config_data->iproutes_len); } -NMCSProviderGetConfigIfaceData *nmcs_provider_get_config_iface_data_new(gboolean was_requested); +NMCSProviderGetConfigIfaceData *nmcs_provider_get_config_iface_data_create(GHashTable *iface_datas, + gboolean was_requested, + const char *hwaddr); + +/*****************************************************************************/ + +typedef struct { + /* A dictionary of (const char *) -> (NMCSProviderGetConfigIfaceData *). + * This is the per-interface result of get_config(). + * + * The key is the same pointer as NMCSProviderGetConfigIfaceData's hwaddr. */ + GHashTable *iface_datas; + + /* The number of iface_datas that are nmcs_provider_get_config_iface_data_is_valid(). */ + guint num_valid_ifaces; + + /* the number of IPv4 addresses over all valid iface_datas. */ + guint num_ipv4s; + + guint n_iface_datas; + + /* The sorted value of @iface_datas, sorted by iface_idx. + * + * Not found entries (iface_idx == -1) are sorted at the end. */ + const NMCSProviderGetConfigIfaceData *const *iface_datas_arr; + +} NMCSProviderGetConfigResult; + +void nmcs_provider_get_config_result_free(NMCSProviderGetConfigResult *result); + +NM_AUTO_DEFINE_FCN0(NMCSProviderGetConfigResult *, + _nm_auto_free_nmcs_provider_get_config_result, + nmcs_provider_get_config_result_free); +#define nm_auto_free_nmcs_provider_get_config_result \ + nm_auto(_nm_auto_free_nmcs_provider_get_config_result) + +/*****************************************************************************/ typedef struct { GTask *task; @@ -124,7 +164,7 @@ void nmcs_provider_get_config(NMCSProvider * provider, GAsyncReadyCallback callback, gpointer user_data); -GHashTable * +NMCSProviderGetConfigResult * nmcs_provider_get_config_finish(NMCSProvider *provider, GAsyncResult *result, GError **error); #endif /* __NMCS_PROVIDER_H__ */