Thomas Haller 2021-09-16 17:35:18 +02:00
commit df7d1f94b7
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
8 changed files with 347 additions and 200 deletions

View file

@ -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 <literal>org.freedesktop.nm-cloud-setup.skip=yes</literal>,
it is skipped.</para>
<para>Then, the tool will change the runtime configuration of the device.
<para>If only one interface and one address is configured, then the tool does nothing
and leaves the automatic configuration that was obtained via DHCP.</para>
<para>Otherwise, the tool will change the runtime configuration of the device.
<itemizedlist>
<listitem>
<para>Add static IPv4 addresses for all the configured addresses from <literal>local-ipv4s</literal> with
@ -267,15 +269,25 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst
<para>Choose a route table 30400 + the index of the interface and
add a default route <literal>0.0.0.0/0</literal>. The gateway
is the first IP address in the CIDR subnet block. For
example, we might get a route <literal>"0.0.0.0/0 172.16.5.1 10 table=30401"</literal>.</para>
example, we might get a route <literal>"0.0.0.0/0 172.16.5.1 10 table=30400"</literal>.</para>
<para>Also choose a route table 30200 + the interface index. This
contains a direct routes to the subnets of this interface.</para>
</listitem>
<listitem>
<para>Finally, add a policy routing rule for each address. For example
<literal>"priority 30401 from 172.16.5.3/32 table 30401, priority 30401 from 172.16.5.4/32 table 30401"</literal>.</para>
<literal>"priority 30200 from 172.16.5.3/32 table 30200, priority 30200 from 172.16.5.4/32 table 30200"</literal>.
and
<literal>"priority 30400 from 172.16.5.3/32 table 30400, priority 30400 from 172.16.5.4/32 table 30400"</literal>
The 30200+ rules select the table to reach the subnet directly, while the 30400+ rules use the
default route. Also add a rule
<literal>"priority 30350 table main suppress_prefixlength 0"</literal>. 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+.</para>
</listitem>
</itemizedlist>
With above example, this roughly corresponds for interface <literal>eth0</literal> to
<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>.
<command>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"</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 on disk is not affected.
@ -360,14 +372,8 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst
</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>
<para>Then the tool configures the system like doing for AWS environment. That is, using source based policy routing
with the tables/rules 30200/30400.</para>
</listitem>
</itemizedlist>
</refsect2>
@ -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.</para>
</listitem>
<listitem>
<para>Then for each interface fetch <literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/vpc-cidr-block</literal>
, <literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/private-ipv4s</literal> and
<literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/netmask</literal>.
<para>Then for each interface fetch <literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/vpc-cidr-block</literal>,
<literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/private-ipv4s</literal>,
<literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/netmask</literal> and
<literal>http://100.100.100.200/2016-01-01/meta-data/network/interfaces/macs/$MAC/gateway</literal>.
Thereby we get a list of private IPv4 addresses, one CIDR subnet block and private IPv4 addresses prefix.</para>
</listitem>
<listitem>
@ -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 <literal>org.freedesktop.nm-cloud-setup.skip=yes</literal>,
it is skipped.</para>
<para>Then, the tool will change the runtime configuration of the device.
<itemizedlist>
<listitem>
<para>Add static IPv4 addresses for all the configured addresses from <literal>private-ipv4s</literal> with
prefix length according to <literal>netmask</literal>. For example,
we might have here 2 IP addresses like <literal>"10.0.0.150/24,10.0.0.152/24"</literal>.</para>
</listitem>
<listitem>
<para>Choose a route table 30400 + the index of the interface and
add a default route <literal>0.0.0.0/0</literal>. The gateway
is the default gateway retrieved from metadata server. For
example, we might get a route <literal>"0.0.0.0/0 10.0.0.253 10 table=30400"</literal>.</para>
</listitem>
<listitem>
<para>Finally, add a policy routing rule for each address. For example
<literal>"priority 30400 from 10.0.0.150/32 table 30400, priority 30400 from 10.0.0.152/32 table 30400"</literal>.</para>
</listitem>
</itemizedlist>
With above example, this roughly corresponds for interface <literal>eth0</literal> to
<command>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"</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 on disk is not affected.
</para>
it is skipped. Also, there is only one interface and one IP address, the tool does nothing.</para>
<para>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.</para>
</listitem>
</itemizedlist>
</refsect2>

View file

@ -4,6 +4,8 @@
#include "libnm-client-aux-extern/nm-libnm-aux.h"
#include <linux/rtnetlink.h>
#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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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__ */