From a67463a4c688d439e3e7595c23df645c2cdad3fd Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Mon, 30 Sep 2024 10:15:59 +0100 Subject: [PATCH 1/8] parse: new structure for "passthrough" The networkmanager.passthrough section stores raw keyfile information using a format that can be tricky to interpret. It will concatenate the group and key names separated by dots. Unfortunately, Network Manager accepts multiple dots in the middle of both the group and key names. Because of that it's hard, if even possible, to determine where the strings must be split. Ex: networkmanager: passthrough: group_name.key_name: value1 group_name.key_name1: value2 The new format stores the same information in YAML mappings. By using this format, parsing the group and key names is not needed anymore. Ex: networkmanager: passthrough: group_name: key_name1: value1 key_name2: value2 The datalist data structure was replaced by hash tables. The entire passthrough section is represented by a hash table indexed by the group name. Each entry is also a hash table representing all the key/value entries and it's indexed by the key name. --- src/abi.h | 2 +- src/netplan.c | 18 +++-- src/nm.c | 79 ++++++++++------------ src/parse-nm.c | 25 ++++--- src/parse.c | 170 ++++++++++++++++++++++++++--------------------- src/types.c | 19 +++++- src/validation.c | 9 ++- 7 files changed, 186 insertions(+), 136 deletions(-) diff --git a/src/abi.h b/src/abi.h index b4b19453c..7c3505474 100644 --- a/src/abi.h +++ b/src/abi.h @@ -211,7 +211,7 @@ typedef struct netplan_backend_settings { char *uuid; char *stable_id; char *device; - GData* passthrough; /* See g_datalist* functions */ + GHashTable* passthrough; } NetplanBackendSettings; typedef struct netplan_vxlan NetplanVxlan; diff --git a/src/netplan.c b/src/netplan.c index d6ab84902..a119eb874 100644 --- a/src/netplan.c +++ b/src/netplan.c @@ -340,14 +340,24 @@ typedef struct { } _passthrough_handler_data; STATIC void -_passthrough_handler(GQuark key_id, gpointer value, gpointer user_data) +_passthrough_handler_key_value(gpointer key, gpointer value, gpointer user_data) { _passthrough_handler_data *d = user_data; - const gchar* key = g_quark_to_string(key_id); YAML_NONNULL_STRING(d->event, d->emitter, key, value); err_path: return; // LCOV_EXCL_LINE } +STATIC void +_passthrough_handler(gpointer key, gpointer value, gpointer user_data) +{ + _passthrough_handler_data *d = user_data; + YAML_SCALAR_PLAIN(d->event, d->emitter, key); + YAML_MAPPING_OPEN(d->event, d->emitter); + g_hash_table_foreach(value, _passthrough_handler_key_value, user_data); + YAML_MAPPING_CLOSE(d->event, d->emitter); +err_path: return; // LCOV_EXCL_LINE +} + STATIC gboolean write_backend_settings(yaml_event_t* event, yaml_emitter_t* emitter, NetplanBackendSettings s) { if (s.uuid || s.name || s.passthrough) { @@ -356,13 +366,13 @@ write_backend_settings(yaml_event_t* event, yaml_emitter_t* emitter, NetplanBack YAML_NONNULL_STRING(event, emitter, "uuid", s.uuid); YAML_NONNULL_STRING(event, emitter, "name", s.name); - if (s.passthrough) { + if (s.passthrough != NULL && g_hash_table_size(s.passthrough) > 0) { YAML_SCALAR_PLAIN(event, emitter, "passthrough"); YAML_MAPPING_OPEN(event, emitter); _passthrough_handler_data d; d.event = event; d.emitter = emitter; - g_datalist_foreach(&s.passthrough, _passthrough_handler, &d); + g_hash_table_foreach(s.passthrough, _passthrough_handler, &d); YAML_MAPPING_CLOSE(event, emitter); } YAML_MAPPING_CLOSE(event, emitter); diff --git a/src/nm.c b/src/nm.c index 3dbb85bfc..108af70fe 100644 --- a/src/nm.c +++ b/src/nm.c @@ -98,8 +98,9 @@ type_str(const NetplanNetDefinition* def) case NETPLAN_DEF_TYPE_NM: /* needs to be overriden by passthrough "connection.type" setting */ g_assert(def->backend_settings.passthrough != NULL); - GData *passthrough = def->backend_settings.passthrough; - return g_datalist_get_data(&passthrough, "connection.type"); + GHashTable *passthrough = def->backend_settings.passthrough; + GHashTable* connection = g_hash_table_lookup(passthrough, "connection"); + return g_hash_table_lookup(connection, "type"); // LCOV_EXCL_START default: g_assert_not_reached(); @@ -566,50 +567,42 @@ write_nm_vxlan_parameters(const NetplanNetDefinition* def, GKeyFile* kf) * "backend_settings.passthrough" and inject them into the keyfile as-is. */ STATIC void -write_fallback_key_value(GQuark key_id, gpointer value, gpointer user_data) +write_fallback_key_value(gpointer group, gpointer value, gpointer user_data) { GKeyFile *kf = user_data; - gchar* val = value; - /* Group name may contain dots, but key name may not. - * The "tc" group is a special case, where it is the other way around, e.g.: - * tc->qdisc.root - * tc->tfilter.ffff: */ - const gchar* key = g_quark_to_string(key_id); - gchar **group_key = g_strsplit(key, ".", -1); - guint len = g_strv_length(group_key); - g_autofree gchar* old_key = NULL; - gboolean has_key = FALSE; - g_autofree gchar* k = NULL; - g_autofree gchar* group = NULL; - if (!g_strcmp0(group_key[0], "tc") && len > 2) { - k = g_strconcat(group_key[1], ".", group_key[2], NULL); - group = g_strdup(group_key[0]); - } else { - k = group_key[len-1]; - group_key[len-1] = NULL; //remove key from array - group = g_strjoinv(".", group_key); //re-combine group parts - } + GHashTableIter iter; + GHashTable* group_settings = value; + gpointer k, v; - has_key = g_key_file_has_key(kf, group, k, NULL); - old_key = g_key_file_get_string(kf, group, k, NULL); - g_key_file_set_string(kf, group, k, val); - /* delete the placeholder key, if this was just an empty group */ - if (!g_strcmp0(k, NETPLAN_NM_EMPTY_GROUP)) - g_key_file_remove_key(kf, group, k, NULL); - /* handle differing defaults: - * ipv6.ip6-privacy is "-1 (unknown)" by default in NM, it is "0 (off)" in netplan */ - else if (g_strcmp0(key, "ipv6.ip6-privacy") == 0 && g_strcmp0(val, "-1") == 0) { - g_debug("NetworkManager: default override: clearing %s.%s", group, k); - g_key_file_remove_key(kf, group, k, NULL); - } else if (!has_key) { - g_debug("NetworkManager: passing through fallback key: %s.%s=%s", group, k, val); - g_key_file_set_comment(kf, group, k, "Netplan: passthrough setting", NULL); - } else if (g_strcmp0(val, old_key) != 0) { - g_debug("NetworkManager: fallback override: %s.%s=%s", group, k, val); - g_key_file_set_comment(kf, group, k, "Netplan: passthrough override", NULL); + /* + * An empty hash table means it's an empty group. + * Here we add and remove a bogus key so the group is created in the kf + */ + if (g_hash_table_size(group_settings) == 0) { + g_key_file_set_string(kf, group, NETPLAN_NM_EMPTY_GROUP, ""); + g_key_file_remove_key(kf, group, NETPLAN_NM_EMPTY_GROUP, NULL); + return; } - g_strfreev(group_key); + g_hash_table_iter_init(&iter, group_settings); + + while (g_hash_table_iter_next(&iter, &k, &v)) { + gboolean has_key = g_key_file_has_key(kf, group, k, NULL); + g_autofree gchar* old_key = g_key_file_get_string(kf, group, k, NULL); + g_key_file_set_string(kf, group, k, v); + /* handle differing defaults: + * ipv6.ip6-privacy is "-1 (unknown)" by default in NM, it is "0 (off)" in netplan */ + if (g_strcmp0(k, "ip6-privacy") == 0 && g_strcmp0(v, "-1") == 0) { + g_debug("NetworkManager: default override: clearing %s.%s", (gchar*)group, (gchar*)k); + g_key_file_remove_key(kf, group, k, NULL); + } else if (!has_key) { + g_debug("NetworkManager: passing through fallback key: %s.%s=%s", (gchar*)group, (gchar*)k, (gchar*)v); + g_key_file_set_comment(kf, group, k, "Netplan: passthrough setting", NULL); + } else if (g_strcmp0(v, old_key) != 0) { + g_debug("NetworkManager: fallback override: %s.%s=%s", (gchar*)group, (gchar*)k, (gchar*)v); + g_key_file_set_comment(kf, group, k, "Netplan: passthrough override", NULL); + } + } } /** @@ -932,7 +925,7 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, g_debug("NetworkManager: using keyfile passthrough mode"); /* Write all key-value pairs from the hashtable into the keyfile, * potentially overriding existing values, if not fully supported. */ - g_datalist_foreach((GData**)&def->backend_settings.passthrough, write_fallback_key_value, kf); + g_hash_table_foreach(def->backend_settings.passthrough, write_fallback_key_value, kf); } g_autofree char* escaped_netdef_id = g_uri_escape_string(def->id, NULL, TRUE); @@ -970,7 +963,7 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, * AP passthrough values have higher priority than ND passthrough, * because they are more specific and bound to the current SSID's * NM connection profile. */ - g_datalist_foreach((GData**)&ap->backend_settings.passthrough, write_fallback_key_value, kf); + g_hash_table_foreach(ap->backend_settings.passthrough, write_fallback_key_value, kf); } } else { /* TODO: make use of netplan_netdef_get_output_filename() */ diff --git a/src/parse-nm.c b/src/parse-nm.c index 3708f594b..67f87a7ad 100644 --- a/src/parse-nm.c +++ b/src/parse-nm.c @@ -448,39 +448,44 @@ parse_bond_arp_ip_targets(GKeyFile* kf, GArray **targets_arr) /* Read the key-value pairs from the keyfile and pass them through to a map */ STATIC void -read_passthrough(GKeyFile* kf, GData** list) +read_passthrough(GKeyFile* kf, GHashTable** list) { gchar **groups = NULL; gchar **keys = NULL; - gchar *group_key = NULL; gchar *value = NULL; gsize klen = 0; gsize glen = 0; + GHashTable* group; - if (!*list) - g_datalist_init(list); + if (*list == NULL) { + *list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } groups = g_key_file_get_groups(kf, &glen); - if (groups) { + if (groups != NULL) { for (unsigned i = 0; i < glen; ++i) { klen = 0; + group = g_hash_table_lookup(*list, groups[i]); + if (group == NULL) { + group = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + } keys = g_key_file_get_keys(kf, groups[i], &klen, NULL); if (klen == 0) { /* empty group */ - g_datalist_set_data_full(list, g_strconcat(groups[i], ".", NETPLAN_NM_EMPTY_GROUP, NULL), g_strdup(""), g_free); + g_hash_table_insert(*list, g_strdup(groups[i]), group); + g_strfreev(keys); continue; } for (unsigned j = 0; j < klen; ++j) { value = g_key_file_get_string(kf, groups[i], keys[j], NULL); - if (!value) { + if (value == NULL) { // LCOV_EXCL_START g_warning("netplan: Keyfile: cannot read value of %s.%s", groups[i], keys[j]); continue; // LCOV_EXCL_STOP } - group_key = g_strconcat(groups[i], ".", keys[j], NULL); - g_datalist_set_data_full(list, group_key, value, g_free); - g_free(group_key); + g_hash_table_insert(group, g_strdup(keys[j]), value); } + g_hash_table_insert(*list, g_strdup(groups[i]), group); g_strfreev(keys); } g_strfreev(groups); diff --git a/src/parse.c b/src/parse.c index 6500c7a9c..ed5a038e6 100644 --- a/src/parse.c +++ b/src/parse.c @@ -527,43 +527,116 @@ handle_generic_map(NetplanParser *npp, yaml_node_t* node, const char* key_prefix } /* - * Handler for setting a DataList field from a mapping node, inside a given struct + * Handle legacy networkmanager.passthrough format: + * group_name.key: value + * + * See https://github.com/canonical/netplan/pull/522 +*/ +STATIC void +handle_passthrough_legacy_scalar_settings(gchar* key, yaml_node_t* settings, GHashTable* groups) +{ + g_autofree char* escaped_value = g_strescape(scalar(settings), STRESCAPE_EXCEPTIONS); + + /* Group name may contain dots, but key name may not. + * The "tc" group is a special case, where it is the other way around, e.g.: + * tc->qdisc.root + * tc->tfilter.ffff: + */ + + gchar **group_key = g_strsplit(key, ".", -1); + guint len = g_strv_length(group_key); + g_autofree gchar* old_key = NULL; + g_autofree gchar* k = NULL; + g_autofree gchar* group = NULL; + + if (len < 2) { + g_warning("NetworkManager: passthrough key '%s' format is invalid, should be 'group.key'.", key); + g_strfreev(group_key); + return; + } + if (!g_strcmp0(group_key[0], "tc") && len > 2) { + k = g_strconcat(group_key[1], ".", group_key[2], NULL); + group = g_strdup(group_key[0]); + } else { + k = group_key[len-1]; + group_key[len-1] = NULL; //remove key from array + group = g_strjoinv(".", group_key); //re-combine group parts + } + + if (!g_hash_table_contains(groups, group)) { + GHashTable *keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + /* Only add to the hash map if it's not the empty group */ + if (g_strcmp0(k, NETPLAN_NM_EMPTY_GROUP)) { + g_hash_table_insert(keys, g_strdup(k), g_strdup(escaped_value)); + } + g_hash_table_insert(groups, g_strdup(group), keys); + } else { + GHashTable *keys = g_hash_table_lookup(groups, group); + + /* If we find the group again and it's empty, drop all the items from the group */ + if (!g_strcmp0(k, NETPLAN_NM_EMPTY_GROUP)) { + g_hash_table_remove_all(keys); + } else { + g_hash_table_insert(keys, g_strdup(k), g_strdup(escaped_value)); + } + } + g_strfreev(group_key); +} + +/* + * Handler for setting a Hashmap field from a mapping node, inside a given struct * @entryptr: pointer to the beginning of the to-be-modified data structure * @data: offset into entryptr struct where the boolean field to write is located */ STATIC gboolean -handle_generic_datalist(NetplanParser *npp, yaml_node_t* node, const char* key_prefix, void* entryptr, const void* data, GError** error) +handle_passthrough(NetplanParser *npp, yaml_node_t* node, const char* key_prefix, void* entryptr, const void* data, GError** error) { guint offset = GPOINTER_TO_UINT(data); - GData** list = (GData**) ((void*) entryptr + offset); - if (!*list) - g_datalist_init(list); + GHashTable** groups = (GHashTable**) ((void*) entryptr + offset); + if (*groups == NULL) { + *groups = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { yaml_node_t* key, *value; g_autofree char* full_key = NULL; g_autofree char* escaped_key = NULL; - g_autofree char* escaped_value = NULL; key = yaml_document_get_node(&npp->doc, entry->key); value = yaml_document_get_node(&npp->doc, entry->value); - assert_type(npp, key, YAML_SCALAR_NODE); - assert_type(npp, value, YAML_SCALAR_NODE); - escaped_key = g_strescape(scalar(key), STRESCAPE_EXCEPTIONS); - escaped_value = g_strescape(scalar(value), STRESCAPE_EXCEPTIONS); - if (npp->null_fields && key_prefix) { + if (npp->null_fields != NULL && key_prefix != NULL) { full_key = g_strdup_printf("%s\t%s", key_prefix, escaped_key); - if (g_hash_table_contains(npp->null_fields, full_key)) + if (g_hash_table_contains(npp->null_fields, full_key)) { continue; + } } - g_datalist_id_set_data_full(list, g_quark_from_string(escaped_key), - g_strdup(escaped_value), g_free); + if (value->type == YAML_SCALAR_NODE) { + handle_passthrough_legacy_scalar_settings(escaped_key, value, *groups); + } else if (value->type == YAML_MAPPING_NODE) { + /* + * Handle new networkmanager.passthrough format: + * group_name: + * key: value + * + * See https://github.com/canonical/netplan/pull/522 + */ + GHashTable* group = g_hash_table_lookup(*groups, escaped_key); + if (group == NULL) { + group = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(*groups, g_strdup(escaped_key), group); + } + handle_generic_map(npp, value, full_key, &group, NULL, error); + if (*error != NULL) { + g_debug("Passthrough handling error, ignoring...: %s", (*error)->message); + netplan_error_clear(error); + } + } } - mark_data_as_dirty(npp, list); + mark_data_as_dirty(npp, groups); return TRUE; } @@ -864,47 +937,11 @@ handle_netdef_use_domains(NetplanParser* npp, yaml_node_t* node, const void* dat return TRUE; } -/* - * Check if the passthrough key format is incorrect and remove it from the list. - * user_data is expected to contain a pointer to the GData list. - */ -STATIC void -validate_kf_group_key(GQuark key_id, __unused gpointer value, gpointer user_data) -{ - GArray* bad_keys = user_data; - const gchar* key = g_quark_to_string(key_id); - gchar** group_key = g_strsplit(key, ".", -1); - if (g_strv_length(group_key) < 2) { - g_warning("NetworkManager: passthrough key '%s' format is invalid, should be 'group.key'.", key); - g_array_append_val(bad_keys, key_id); - } - g_strfreev(group_key); -} - STATIC gboolean -handle_netdef_passthrough_datalist(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error) +handle_netdef_passthrough_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error) { g_assert(npp->current.netdef); - gboolean ret = handle_generic_datalist(npp, node, key_prefix, npp->current.netdef, data, error); - - GData** list = &npp->current.netdef->backend_settings.passthrough; - GArray* bad_keys = g_array_new(FALSE, FALSE, sizeof(GQuark)); - - /* Validate and remove passthrough keys that are not in the - * expected format (group.key) - */ - g_datalist_foreach(list, validate_kf_group_key, bad_keys); - - for (unsigned int i = 0; i < bad_keys->len; i++) { - GQuark bad_quark = g_array_index(bad_keys, GQuark, i); - g_datalist_id_remove_data(list, bad_quark); - } - - g_array_free(bad_keys, TRUE); - - if (*list == NULL) { - g_datalist_clear(list); - } + gboolean ret = handle_passthrough(npp, node, key_prefix, npp->current.netdef, data, error); npp->current.netdef->has_backend_settings_nm = TRUE; @@ -1102,29 +1139,10 @@ handle_ap_backend_settings_str(NetplanParser* npp, yaml_node_t* node, const void } STATIC gboolean -handle_access_point_datalist(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error) +handle_access_point_passthrough_mapping(NetplanParser* npp, yaml_node_t* node, const char* key_prefix, const void* data, GError** error) { g_assert(npp->current.access_point != NULL); - gboolean ret = handle_generic_datalist(npp, node, key_prefix, npp->current.access_point, data, error); - - GData** list = &npp->current.access_point->backend_settings.passthrough; - GArray* bad_keys = g_array_new(FALSE, FALSE, sizeof(GQuark)); - - /* Validate and remove passthrough keys that are not in the - * expected format (group.key) - */ - g_datalist_foreach(list, validate_kf_group_key, bad_keys); - - for (unsigned int i = 0; i < bad_keys->len; i++) { - GQuark bad_quark = g_array_index(bad_keys, GQuark, i); - g_datalist_id_remove_data(list, bad_quark); - } - - g_array_free(bad_keys, TRUE); - - if (*list == NULL) { - g_datalist_clear(list); - } + gboolean ret = handle_passthrough(npp, node, key_prefix, npp->current.access_point, data, error); npp->current.netdef->has_backend_settings_nm = TRUE; @@ -1241,7 +1259,7 @@ static const mapping_entry_handler nm_backend_settings_handlers[] = { {"stable-id", YAML_SCALAR_NODE, {.generic=handle_netdef_backend_settings_str}, netdef_offset(backend_settings.stable_id)}, {"device", YAML_SCALAR_NODE, {.generic=handle_netdef_backend_settings_str}, netdef_offset(backend_settings.device)}, /* Fallback mode, to support all NM settings of the NetworkManager netplan backend */ - {"passthrough", YAML_MAPPING_NODE, {.map={.custom=handle_netdef_passthrough_datalist}}, netdef_offset(backend_settings.passthrough)}, + {"passthrough", YAML_MAPPING_NODE, {.map={.custom=handle_netdef_passthrough_mapping}}, netdef_offset(backend_settings.passthrough)}, {NULL} }; @@ -1252,7 +1270,7 @@ static const mapping_entry_handler ap_nm_backend_settings_handlers[] = { {"stable-id", YAML_SCALAR_NODE, {.generic=handle_ap_backend_settings_str}, access_point_offset(backend_settings.stable_id)}, {"device", YAML_SCALAR_NODE, {.generic=handle_ap_backend_settings_str}, access_point_offset(backend_settings.device)}, /* Fallback mode, to support all NM settings of the NetworkManager netplan backend */ - {"passthrough", YAML_MAPPING_NODE, {.map={.custom=handle_access_point_datalist}}, access_point_offset(backend_settings.passthrough)}, + {"passthrough", YAML_MAPPING_NODE, {.map={.custom=handle_access_point_passthrough_mapping}}, access_point_offset(backend_settings.passthrough)}, {NULL} }; diff --git a/src/types.c b/src/types.c index 7a1c20ed2..99944795d 100644 --- a/src/types.c +++ b/src/types.c @@ -169,6 +169,21 @@ reset_ip_rule(NetplanIPRule* ip_rule) ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC; } +STATIC void +reset_passthrough(GHashTable* passthrough) +{ + GHashTableIter iter; + GHashTable* group; + gchar* group_name; + + g_hash_table_iter_init(&iter, passthrough); + + while (g_hash_table_iter_next(&iter, (gpointer) &group_name, (gpointer) &group)) { + g_hash_table_destroy(group); + } + g_hash_table_destroy(passthrough); +} + /* Reset a backend settings object. */ STATIC void reset_backend_settings(NetplanBackendSettings* settings) @@ -177,7 +192,9 @@ reset_backend_settings(NetplanBackendSettings* settings) FREE_AND_NULLIFY(settings->uuid); FREE_AND_NULLIFY(settings->stable_id); FREE_AND_NULLIFY(settings->device); - g_datalist_clear(&settings->passthrough); + if (settings->passthrough != NULL) { + reset_passthrough(settings->passthrough); + } } STATIC void diff --git a/src/validation.c b/src/validation.c index a0371a8fe..692f6a8ae 100644 --- a/src/validation.c +++ b/src/validation.c @@ -407,7 +407,14 @@ validate_netdef_grammar(const NetplanParser* npp, NetplanNetDefinition* nd, GErr // LCOV_EXCL_STOP } - if (nd->type == NETPLAN_DEF_TYPE_NM && (!nd->backend_settings.passthrough || !g_datalist_get_data(&nd->backend_settings.passthrough, "connection.type"))) + GHashTable* key = NULL; + if (nd->backend_settings.passthrough != NULL) { + GHashTable* group = g_hash_table_lookup(nd->backend_settings.passthrough, "connection"); + if (group != NULL) { + key = g_hash_table_lookup(group, "type"); + } + } + if (nd->type == NETPLAN_DEF_TYPE_NM && key == NULL) return yaml_error(npp, NULL, error, "%s: network type 'nm-devices:' needs to provide a 'connection.type' via passthrough", nd->id); if (npp->current.netdef) From a2d5e8c4d822dd04aaab1b4372cb064d72de5171 Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Mon, 30 Sep 2024 17:12:04 +0100 Subject: [PATCH 2/8] tests: adapt tests to new passthrough format --- tests/cli/test_get_set.py | 102 ++++++++- tests/generator/test_modems.py | 38 ++-- tests/generator/test_passthrough.py | 219 +++++++++++++++++- tests/parser/test_keyfile.py | 336 +++++++++++++++++----------- 4 files changed, 532 insertions(+), 163 deletions(-) diff --git a/tests/cli/test_get_set.py b/tests/cli/test_get_set.py index 6d8b93ae3..3431c5466 100644 --- a/tests/cli/test_get_set.py +++ b/tests/cli/test_get_set.py @@ -464,7 +464,7 @@ def test_set_delete_access_point(self): out = yaml.safe_load(f) self.assertNotIn('Joe\'s Home', out['network']['wifis']['wl0']['access-points']) - def test_set_delete_nm_passthrough(self): + def test_set_delete_nm_ap_passthrough(self): with open(self.path, 'w') as f: f.write('''network: version: 2 @@ -479,15 +479,40 @@ def test_set_delete_nm_passthrough(self): networkmanager: name: "myid with spaces" passthrough: - connection.permissions: "" - ipv4.dns-search: ""''') + connection: + permissions: "" + ipv4: + dns-search: ""''') ap_key = 'network.wifis.wlan0.access-points.SOME-SSID' - self._set([ap_key+'.networkmanager.passthrough.connection\\.permissions=null']) + self._set([ap_key+'.networkmanager.passthrough.connection.permissions=null']) with open(self.path, 'r') as f: out = yaml.safe_load(f) ap = out['network']['wifis']['wlan0']['access-points']['SOME-SSID'] - self.assertNotIn('connection.permissions', ap['networkmanager']['passthrough']) - self.assertEqual('', ap['networkmanager']['passthrough']['ipv4.dns-search']) + self.assertNotIn('permissions', ap['networkmanager']['passthrough']['connection']) + self.assertEqual('', ap['networkmanager']['passthrough']['ipv4']['dns-search']) + + def test_set_delete_nm_passthrough(self): + with open(self.path, 'w') as f: + f.write('''network: + version: 2 + ethernets: + eth0: + renderer: NetworkManager + networkmanager: + name: "myid with spaces" + passthrough: + connection: + permissions: "" + ipv4: + dns-search: ""''') + ap_key = 'network.ethernets.eth0' + self._set([ap_key+'.networkmanager.passthrough.connection.permissions=null']) + self._set([ap_key+'.networkmanager.passthrough.ipv4=null']) + with open(self.path, 'r') as f: + out = yaml.safe_load(f) + eth0 = out['network']['ethernets']['eth0'] + self.assertNotIn('permissions', eth0['networkmanager']['passthrough']['connection']) + self.assertNotIn('ipv4', eth0['networkmanager']['passthrough']) def test_set_delete_bridge_subparams(self): with open(self.path, 'w') as f: @@ -756,3 +781,68 @@ def test_get_yaml_document_end_failure(self): # this shall not throw any (YAML DOCUMENT-END) exception out = yaml.safe_load(self._get(['ethernets.eth0'])) self.assertListEqual(['match', 'dhcp4', 'set-name', 'mtu', 'virtual-function-count'], list(out)) + + def test_get_passthrough_old_format_merging(self): + + # Test if the YAML we are emitting is in the expected new passthrough format + # when the same groups (especially empty groups) are found more than once. + # Also test if merging is working. + with open(os.path.join(self.workdir.name, 'etc', 'netplan', 'a.yaml'), 'w') as f: + f.write('''network: + version: 2 + vlans: + NM-afe79ef7-67e0-48ad-9f2a-686c85b4f538: + renderer: NetworkManager + id: 123 + link: "enx00e04c680007" + networkmanager: + uuid: "afe79ef7-67e0-48ad-9f2a-686c85b4f538" + name: "vlan-test" + passthrough: + ipv6.addr-gen-mode: "default" + ipv6.ip6-privacy: "-1" + ethernet._: "" + vlan.flags: "2" + proxy._: ""''') + + with open(os.path.join(self.workdir.name, 'etc', 'netplan', 'b.yaml'), 'w') as f: + f.write('''network: + version: 2 + vlans: + NM-afe79ef7-67e0-48ad-9f2a-686c85b4f538: + renderer: NetworkManager + id: 123 + link: "enx00e04c680007" + networkmanager: + uuid: "afe79ef7-67e0-48ad-9f2a-686c85b4f538" + name: "vlan-test" + passthrough: + ipv6.addr-gen-mode: "default" + ipv6.ip6-privacy: "-1" + ethernet._: "" + vlan.flags: "1" + proxy._: ""''') + + with open(os.path.join(self.workdir.name, 'etc', 'netplan', 'c.yaml'), 'w') as f: + f.write('''network: + version: 2 + vlans: + NM-afe79ef7-67e0-48ad-9f2a-686c85b4f538: + renderer: NetworkManager + id: 123 + link: "enx00e04c680007" + networkmanager: + uuid: "afe79ef7-67e0-48ad-9f2a-686c85b4f538" + name: "vlan-test" + passthrough: + ipv6._: "" + ethernet._: "" + vlan.flags: "1" + proxy._: ""''') + + out = yaml.safe_load(self._get(['vlans.NM-afe79ef7-67e0-48ad-9f2a-686c85b4f538.networkmanager.passthrough'])) + self.assertDictEqual({'ethernet': {}, + 'ipv6': {}, + 'proxy': {}, + 'vlan': {'flags': '1'} + }, out) diff --git a/tests/generator/test_modems.py b/tests/generator/test_modems.py index b66144efc..867b14c49 100644 --- a/tests/generator/test_modems.py +++ b/tests/generator/test_modems.py @@ -380,17 +380,21 @@ def test_modem_nm_integration_gsm_cdma(self): uuid: a08c5805-7cf5-43f7-afb9-12cb30f6eca3 name: "T-Mobile Funkadelic 2" passthrough: - connection.type: "bluetooth" - gsm.apn: "internet2.voicestream.com" - gsm.device-id: "da812de91eec16620b06cd0ca5cbc7ea25245222" - gsm.username: "george.clinton.again" - gsm.sim-operator-id: "310260" - gsm.pin: "123456" - gsm.sim-id: "89148000000060671234" - gsm.password: "parliament2" - gsm.network-id: "254098" - ipv4.method: "auto" - ipv6.method: "auto"''') + connection: + type: bluetooth + gsm: + apn: internet2.voicestream.com + device-id: da812de91eec16620b06cd0ca5cbc7ea25245222 + username: george.clinton.again + sim-operator-id: 310260 + pin: 123456 + sim-id: 89148000000060671234 + password: parliament2 + network-id: 254098 + ipv4: + method: auto + ipv6: + method: auto''') self.assert_nm({'NM-a08c5805-7cf5-43f7-afb9-12cb30f6eca3': '''[connection] id=T-Mobile Funkadelic 2 #Netplan: passthrough override @@ -400,19 +404,19 @@ def test_modem_nm_integration_gsm_cdma(self): [gsm] apn=internet2.voicestream.com #Netplan: passthrough setting -device-id=da812de91eec16620b06cd0ca5cbc7ea25245222 +password=parliament2 #Netplan: passthrough setting -username=george.clinton.again +network-id=254098 #Netplan: passthrough setting -sim-operator-id=310260 +sim-id=89148000000060671234 #Netplan: passthrough setting pin=123456 #Netplan: passthrough setting -sim-id=89148000000060671234 +username=george.clinton.again #Netplan: passthrough setting -password=parliament2 +sim-operator-id=310260 #Netplan: passthrough setting -network-id=254098 +device-id=da812de91eec16620b06cd0ca5cbc7ea25245222 [ipv4] #Netplan: passthrough override diff --git a/tests/generator/test_passthrough.py b/tests/generator/test_passthrough.py index 8e99af473..91e97b604 100644 --- a/tests/generator/test_passthrough.py +++ b/tests/generator/test_passthrough.py @@ -39,7 +39,7 @@ def test_passthrough_basic(self): passthrough: connection.uuid: 87749f1d-334f-40b2-98d4-55db58965f5f connection.type: ethernet - connection.permissions: ""''') + connection.permissions: ""''', skip_generated_yaml_validation=True) self.assert_nm({'NM-87749f1d-334f-40b2-98d4-55db58965f5f': '''[connection] id=some NM id @@ -58,6 +58,83 @@ def test_passthrough_basic(self): method=ignore '''}, '''[device-netplan.ethernets.NM-87749f1d-334f-40b2-98d4-55db58965f5f] match-device=type:ethernet +managed=1\n\n''') + + def test_passthrough_basic_mapping(self): + self.generate('''network: + version: 2 + ethernets: + NM-87749f1d-334f-40b2-98d4-55db58965f5f: + renderer: NetworkManager + match: {} + networkmanager: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + name: some NM id + passthrough: + connection: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + type: ethernet + permissions: ""''') + + self.assert_nm({'NM-87749f1d-334f-40b2-98d4-55db58965f5f': '''[connection] +id=some NM id +type=ethernet +uuid=87749f1d-334f-40b2-98d4-55db58965f5f +#Netplan: passthrough setting +permissions= + +[ethernet] +wake-on-lan=0 + +[ipv4] +method=link-local + +[ipv6] +method=ignore +'''}, '''[device-netplan.ethernets.NM-87749f1d-334f-40b2-98d4-55db58965f5f] +match-device=type:ethernet +managed=1\n\n''') + + def test_passthrough_basic_mapping_with_duplication(self): + self.generate('''network: + version: 2 + ethernets: + NM-87749f1d-334f-40b2-98d4-55db58965f5f: + renderer: NetworkManager + match: {} + networkmanager: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + name: some NM id + passthrough: + connection: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + type: ethernet + permissions: "" + group2: + key: a + key: b''', skip_generated_yaml_validation=True) + + self.assert_nm({'NM-87749f1d-334f-40b2-98d4-55db58965f5f': '''[connection] +id=some NM id +type=ethernet +uuid=87749f1d-334f-40b2-98d4-55db58965f5f +#Netplan: passthrough setting +permissions= + +[ethernet] +wake-on-lan=0 + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[group2] +#Netplan: passthrough setting +key=a +'''}, '''[device-netplan.ethernets.NM-87749f1d-334f-40b2-98d4-55db58965f5f] +match-device=type:ethernet managed=1\n\n''') def test_passthrough_wifi(self): @@ -75,6 +152,61 @@ def test_passthrough_wifi(self): passthrough: connection.permissions: "" wifi.ssid: SOME-SSID + "OTHER-SSID": + hidden: true''', skip_generated_yaml_validation=True) + + self.assert_nm({'NM-87749f1d-334f-40b2-98d4-55db58965f5f-SOME-SSID': '''[connection] +id=myid with spaces +type=wifi +uuid=87749f1d-334f-40b2-98d4-55db58965f5f +#Netplan: passthrough setting +permissions= + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[wifi] +ssid=SOME-SSID +mode=infrastructure +''', + 'NM-87749f1d-334f-40b2-98d4-55db58965f5f-OTHER-SSID': '''[connection] +id=netplan-NM-87749f1d-334f-40b2-98d4-55db58965f5f-OTHER-SSID +type=wifi + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[wifi] +ssid=OTHER-SSID +mode=infrastructure +hidden=true +'''}, '''[device-netplan.wifis.NM-87749f1d-334f-40b2-98d4-55db58965f5f] +match-device=type:wifi +managed=1\n\n''') + + def test_passthrough_wifi_mapping(self): + self.generate('''network: + version: 2 + wifis: + NM-87749f1d-334f-40b2-98d4-55db58965f5f: + renderer: NetworkManager + match: {} + access-points: + "SOME-SSID": + networkmanager: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + name: myid with spaces + passthrough: + connection: + permissions: "" + wifi: + ssid: SOME-SSID "OTHER-SSID": hidden: true''') @@ -121,8 +253,9 @@ def test_passthrough_type_nm_devices(self): match: {} networkmanager: passthrough: - connection.uuid: 87749f1d-334f-40b2-98d4-55db58965f5f - connection.type: dummy''') # wokeignore:rule=dummy + connection: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + type: dummy''') # wokeignore:rule=dummy self.assert_nm({'NM-87749f1d-334f-40b2-98d4-55db58965f5f': '''[connection] id=netplan-NM-87749f1d-334f-40b2-98d4-55db58965f5f @@ -149,7 +282,38 @@ def test_passthrough_dotted_group(self): networkmanager: passthrough: connection.type: "wireguard" - wireguard-peer.some-key.endpoint: 1.2.3.4''') + wireguard-peer.some-key.endpoint: 1.2.3.4''', skip_generated_yaml_validation=True) + + self.assert_nm({'dotted-group-test': '''[connection] +id=netplan-dotted-group-test +#Netplan: passthrough setting +type=wireguard + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[wireguard-peer.some-key] +#Netplan: passthrough setting +endpoint=1.2.3.4 +'''}, '''[device-netplan.nm-devices.dotted-group-test] +match-device=type:wireguard +managed=1\n\n''') + + def test_passthrough_dotted_group_mapping(self): + self.generate('''network: + nm-devices: + dotted-group-test: + renderer: NetworkManager + match: {} + networkmanager: + passthrough: + connection: + type: "wireguard" + wireguard-peer.some-key: + endpoint: 1.2.3.4''') self.assert_nm({'dotted-group-test': '''[connection] id=netplan-dotted-group-test @@ -179,7 +343,7 @@ def test_passthrough_dotted_key(self): passthrough: tc.qdisc.root: something tc.qdisc.fff1: ":abc" - tc.filters.test: "test"''') + tc.filters.test: "test"''', skip_generated_yaml_validation=True) self.assert_nm({'dotted-key-test': '''[connection] id=netplan-dotted-key-test @@ -198,9 +362,46 @@ def test_passthrough_dotted_key(self): #Netplan: passthrough setting qdisc.root=something #Netplan: passthrough setting +filters.test=test +#Netplan: passthrough setting qdisc.fff1=:abc +'''}, '''[device-netplan.ethernets.dotted-key-test] +match-device=type:ethernet +managed=1\n\n''') + + def test_passthrough_dotted_key_mapping(self): + self.generate('''network: + ethernets: + dotted-key-test: + renderer: NetworkManager + match: {} + networkmanager: + passthrough: + tc: + qdisc.root: something + qdisc.fff1: ":abc" + filters.test: "test"''') + + self.assert_nm({'dotted-key-test': '''[connection] +id=netplan-dotted-key-test +type=ethernet + +[ethernet] +wake-on-lan=0 + +[ipv4] +method=link-local + +[ipv6] +method=ignore + +[tc] +#Netplan: passthrough setting +qdisc.root=something #Netplan: passthrough setting filters.test=test +#Netplan: passthrough setting +qdisc.fff1=:abc '''}, '''[device-netplan.ethernets.dotted-key-test] match-device=type:ethernet managed=1\n\n''') @@ -215,7 +416,8 @@ def test_passthrough_unsupported_setting(self): "SOME-SSID": # implicit "mode: infrasturcutre" networkmanager: passthrough: - wifi.mode: "mesh"''') + wifi: + mode: "mesh"''') self.assert_nm({'test-SOME-SSID': '''[connection] id=netplan-test-SOME-SSID @@ -243,7 +445,7 @@ def test_passthrough_empty_group(self): match: {} networkmanager: passthrough: - proxy._: ""''') + proxy: {}''') self.assert_nm({'test': '''[connection] id=netplan-test @@ -311,7 +513,8 @@ def test_passthrough_ip6_privacy_default(self): uuid: 626dd384-8b3d-3690-9511-192b2c79b3fd name: "netplan-eth0" passthrough: - "ipv6.ip6-privacy": "-1" + ipv6: + ip6-privacy: -1 ''') self.assert_nm({'eth0': '''[connection] diff --git a/tests/parser/test_keyfile.py b/tests/parser/test_keyfile.py index e178f7729..14e4f89da 100644 --- a/tests/parser/test_keyfile.py +++ b/tests/parser/test_keyfile.py @@ -91,9 +91,12 @@ def test_keyfile_gsm(self): uuid: "{}" name: "T-Mobile Funkadelic 2" passthrough: - gsm.home-only: "true" - ipv4.dns-search: "" - ipv6.dns-search: "" + gsm: + home-only: "true" + ipv4: + dns-search: "" + ipv6: + dns-search: "" '''.format(UUID, UUID)}) def test_keyfile_cdma(self): @@ -165,21 +168,25 @@ def test_keyfile_gsm_via_bluetooth(self): uuid: "{}" name: "T-Mobile Funkadelic 2" passthrough: - connection.type: "bluetooth" - gsm.apn: "internet2.voicestream.com" - gsm.device-id: "da812de91eec16620b06cd0ca5cbc7ea25245222" - gsm.home-only: "true" - gsm.network-id: "254098" - gsm.password: "parliament2" - gsm.pin: "123456" - gsm.sim-id: "89148000000060671234" - gsm.sim-operator-id: "310260" - gsm.username: "george.clinton.again" - ipv4.dns-search: "" - ipv4.method: "auto" - ipv6.dns-search: "" - ipv6.method: "auto" - proxy._: "" + connection: + type: "bluetooth" + proxy: {{}} + gsm: + password: "parliament2" + apn: "internet2.voicestream.com" + home-only: "true" + network-id: "254098" + sim-id: "89148000000060671234" + pin: "123456" + device-id: "da812de91eec16620b06cd0ca5cbc7ea25245222" + sim-operator-id: "310260" + username: "george.clinton.again" + ipv4: + method: "auto" + dns-search: "" + ipv6: + method: "auto" + dns-search: "" '''.format(UUID, UUID)}) def test_keyfile_method_auto(self): @@ -232,9 +239,11 @@ def test_keyfile_method_auto(self): uuid: "{}" name: "Test" passthrough: - ipv4.dns-search: "" - ipv6.dns-search: "" - proxy._: "" + proxy: {{}} + ipv4: + dns-search: "" + ipv6: + dns-search: "" '''.format(UUID, UUID)}) def test_keyfile_fail_validation(self): @@ -332,13 +341,15 @@ def test_keyfile_method_manual(self): uuid: "{}" name: "Test" passthrough: - ipv4.dns-search: "foo.local;bar.remote;" - ipv4.method: "manual" - ipv4.address1: "1.2.3.4/24,8.8.8.8" - ipv6.dns-search: "bar.local" - ipv6.route1: "dead:beef::1/128,2001:1234::2" - ipv6.route1_options: "unknown=invalid," - proxy._: "" + proxy: {{}} + ipv4: + method: "manual" + dns-search: "foo.local;bar.remote;" + address1: "1.2.3.4/24,8.8.8.8" + ipv6: + route1_options: "unknown=invalid," + dns-search: "bar.local" + route1: "dead:beef::1/128,2001:1234::2" '''.format(UUID, UUID)}) def test_keyfile_dummy(self): # wokeignore:rule=dummy @@ -456,8 +467,10 @@ def test_keyfile_type_wifi(self): uuid: "{}" name: "myid with spaces" passthrough: - connection.permissions: "" - ipv4.dns-search: "" + connection: + permissions: "" + ipv4: + dns-search: "" networkmanager: uuid: "{}" name: "myid with spaces" @@ -517,8 +530,10 @@ def _template_keyfile_type_wifi_eap(self, method): uuid: "{}" name: "testnet" passthrough: - connection.permissions: "" - ipv4.dns-search: "" + connection: + permissions: "" + ipv4: + dns-search: "" networkmanager: uuid: "{}" name: "testnet" @@ -580,7 +595,8 @@ def test_keyfile_wifi_eap_leap(self): uuid: "{}" name: "myid with spaces" passthrough: - connection.permissions: "" + connection: + permissions: "" networkmanager: uuid: "{}" name: "myid with spaces" @@ -627,7 +643,8 @@ def test_keyfile_wifi_eap_pwd(self): uuid: "{}" name: "myid with spaces" passthrough: - connection.permissions: "" + connection: + permissions: "" networkmanager: uuid: "{}" name: "myid with spaces" @@ -673,8 +690,10 @@ def test_keyfile_wifi_eap_md5_not_supported(self): uuid: "{}" name: "myid with spaces" passthrough: - connection.permissions: "" - 802-1x.eap: "md5" + connection: + permissions: "" + 802-1x: + eap: "md5" networkmanager: uuid: "{}" name: "myid with spaces" @@ -729,7 +748,8 @@ def test_keyfile_wifi_eap_psk_with_eap(self): uuid: "{}" name: "myid with spaces" passthrough: - connection.permissions: "" + connection: + permissions: "" networkmanager: uuid: "{}" name: "myid with spaces" @@ -754,7 +774,8 @@ def _template_keyfile_type_wifi(self, nd_mode, nm_mode): if nm_mode != nd_mode: wifi_mode = ''' passthrough: - wifi.mode: "{}"'''.format(nm_mode) + wifi: + mode: "{}"'''.format(nm_mode) if nd_mode != 'infrastructure': ap_mode = '\n mode: "%s"' % nd_mode self.assert_netplan({UUID: '''network: @@ -848,7 +869,8 @@ def test_keyfile_wake_on_lan(self): uuid: "{}" name: "myid with spaces" passthrough: - ethernet.wake-on-lan: "2" + ethernet: + wake-on-lan: "2" '''.format(UUID, UUID)}) def test_keyfile_wake_on_lan_nm_default(self): @@ -967,16 +989,21 @@ def test_keyfile_yaml_wifi_hotspot(self): uuid: "{}" name: "Hotspot-1" passthrough: - connection.autoconnect: "false" - connection.permissions: "" - ipv4.dns-search: "" - ipv6.addr-gen-mode: "1" - ipv6.dns-search: "" - wifi.mac-address-blacklist: "" # wokeignore:rule=blacklist - wifi-security.group: "ccmp;" - wifi-security.pairwise: "ccmp;" - wifi-security.proto: "rsn;" - proxy._: "" + connection: + permissions: "" + autoconnect: "false" + wifi: + mac-address-blacklist: "" # wokeignore:rule=blacklist + wifi-security: + proto: "rsn;" + group: "ccmp;" + pairwise: "ccmp;" + ipv4: + dns-search: "" + ipv6: + addr-gen-mode: "1" + dns-search: "" + proxy: {{}} networkmanager: uuid: "{}" name: "Hotspot-1" @@ -1041,7 +1068,8 @@ def test_keyfile_vlan(self): uuid: "{}" name: "netplan-enblue" passthrough: - connection.interface-name: "enblue" + connection: + interface-name: "enblue" '''.format(UUID, UUID)}) def test_keyfile_bridge(self): @@ -1262,10 +1290,12 @@ def test_keyfile_customer_A2(self): uuid: "{}" name: "gsm" passthrough: - ipv4.address1: "10.10.28.159/24" - ipv4.address2: "10.10.164.254/24" - ipv4.address3: "10.10.246.132/24" - ipv6.addr-gen-mode: "1" + ipv4: + address2: "10.10.164.254/24" + address1: "10.10.28.159/24" + address3: "10.10.246.132/24" + ipv6: + addr-gen-mode: "1" '''.format(UUID, UUID)}) def test_keyfile_netplan0103_compat(self): @@ -1359,18 +1389,22 @@ def test_keyfile_netplan0103_compat(self): uuid: "{}" name: "Work Wired" passthrough: - connection.autoconnect: "false" - connection.permissions: "" - connection.timestamp: "305419896" - ethernet.mac-address-blacklist: "" # wokeignore:rule=blacklist - ipv4.address1: "192.168.0.5/24,192.168.0.1" - ipv4.dns-search: "" - ipv4.method: "manual" - ipv4.route4: "3.3.3.3/6,0.0.0.0,4" - ipv4.route4_options: "cwnd=10,mtu=1492,src=1.2.3.4" - ipv6.dns-search: "wallaceandgromit.com;" - ipv6.ip6-privacy: "1" - proxy._: "" + connection: + permissions: "" + autoconnect: "false" + timestamp: "305419896" + proxy: {{}} + ipv4: + route4_options: "cwnd=10,mtu=1492,src=1.2.3.4" + method: "manual" + dns-search: "" + address1: "192.168.0.5/24,192.168.0.1" + route4: "3.3.3.3/6,0.0.0.0,4" + ethernet: + mac-address-blacklist: "" # wokeignore:rule=blacklist + ipv6: + dns-search: "wallaceandgromit.com;" + ip6-privacy: "1" '''.format(UUID, UUID)}) def test_keyfile_tunnel_regression_lp1952967(self): @@ -1413,13 +1447,16 @@ def test_keyfile_tunnel_regression_lp1952967(self): uuid: "{}" name: "IP tunnel connection 1" passthrough: - connection.autoconnect: "false" - connection.interface-name: "gre10" - connection.permissions: "" - ipv4.dns-search: "" - ipv6.dns-search: "" - ipv6.ip6-privacy: "-1" - proxy._: "" + connection: + permissions: "" + autoconnect: "false" + interface-name: "gre10" + proxy: {{}} + ipv4: + dns-search: "" + ipv6: + dns-search: "" + ip6-privacy: "-1" '''.format(UUID, UUID)}) def test_keyfile_ip6_privacy_default_netplan_0104_compat(self): @@ -1451,7 +1488,8 @@ def test_keyfile_ip6_privacy_default_netplan_0104_compat(self): uuid: "{}" name: "Test" passthrough: - ipv6.ip6-privacy: "-1" + ipv6: + ip6-privacy: "-1" '''.format(UUID, UUID)}) def test_keyfile_wpa3_sae(self): @@ -1498,8 +1536,9 @@ def test_keyfile_wpa3_sae(self): uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" name: "test2" passthrough: - ipv6.ip6-privacy: "-1" - proxy._: "" + proxy: {{}} + ipv6: + ip6-privacy: "-1" networkmanager: uuid: "{}" name: "test2" @@ -1563,8 +1602,9 @@ def test_keyfile_wpa3_enterprise_eap_sha256(self): uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" name: "test2" passthrough: - ipv6.ip6-privacy: "-1" - proxy._: "" + proxy: {{}} + ipv6: + ip6-privacy: "-1" networkmanager: uuid: "{}" name: "test2" @@ -1628,8 +1668,9 @@ def test_keyfile_wpa3_enterprise_eap_suite_b_192(self): uuid: "ff9d6ebc-226d-4f82-a485-b7ff83b9607f" name: "test2" passthrough: - ipv6.ip6-privacy: "-1" - proxy._: "" + proxy: {{}} + ipv6: + ip6-privacy: "-1" networkmanager: uuid: "{}" name: "test2" @@ -1687,15 +1728,19 @@ def test_keyfile_dns_search_ip4_ip6_conflict(self): uuid: "{}" name: "Work Wired" passthrough: - connection.autoconnect: "false" - connection.timestamp: "305419896" - ethernet.wake-on-lan: "1" - ipv4.method: "manual" - ipv4.address1: "192.168.0.5/24,192.168.0.1" - ipv6.addr-gen-mode: "1" - ipv6.dns-search: "wallaceandgromit.com;" - ipv6.ip6-privacy: "-1" - proxy._: "" + connection: + autoconnect: "false" + timestamp: "305419896" + proxy: {{}} + ipv4: + method: "manual" + address1: "192.168.0.5/24,192.168.0.1" + ethernet: + wake-on-lan: "1" + ipv6: + addr-gen-mode: "1" + dns-search: "wallaceandgromit.com;" + ip6-privacy: "-1" '''.format(UUID, UUID)}) def test_keyfile_nm_140_default_ethernet_group(self): @@ -1730,13 +1775,15 @@ def test_keyfile_nm_140_default_ethernet_group(self): uuid: "{}" name: "Test Write Bridge Main" passthrough: - ethernet._: "" - bridge._: "" - ipv4.address1: "1.2.3.4/24,1.1.1.1" - ipv4.method: "manual" - ipv6.addr-gen-mode: "default" - ipv6.ip6-privacy: "-1" - proxy._: "" + proxy: {{}} + ipv4: + method: "manual" + address1: "1.2.3.4/24,1.1.1.1" + ethernet: {{}} + bridge: {{}} + ipv6: + addr-gen-mode: "default" + ip6-privacy: "-1" '''.format(UUID)}) def test_multiple_eap_methods(self): @@ -1784,8 +1831,10 @@ def test_multiple_eap_methods(self): uuid: "{}" name: "MyWifi" passthrough: - wifi-security.auth-alg: "open" - 802-1x.eap: "peap;tls" + wifi-security: + auth-alg: "open" + 802-1x: + eap: "peap;tls" networkmanager: uuid: "{}" name: "MyWifi" @@ -1836,7 +1885,8 @@ def test_single_eap_method(self): uuid: "{}" name: "MyWifi" passthrough: - wifi-security.auth-alg: "open" + wifi-security: + auth-alg: "open" networkmanager: uuid: "{}" name: "MyWifi" @@ -1862,7 +1912,8 @@ def test_simple_wireguard(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_with_key(self): @@ -1890,7 +1941,8 @@ def test_wireguard_with_key(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_with_key_and_peer(self): @@ -1928,7 +1980,8 @@ def test_wireguard_with_key_and_peer(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_with_empty_endpoint(self): @@ -1965,7 +2018,8 @@ def test_wireguard_with_empty_endpoint(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_allowed_ips_without_prefix(self): @@ -2008,7 +2062,8 @@ def test_wireguard_allowed_ips_without_prefix(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_with_key_flags(self): @@ -2049,7 +2104,8 @@ def test_wireguard_with_key_flags(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_with_key_all_flags_enabled(self): @@ -2092,7 +2148,8 @@ def test_wireguard_with_key_all_flags_enabled(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_wireguard_with_key_and_peer_without_allowed_ips(self): @@ -2127,7 +2184,8 @@ def test_wireguard_with_key_and_peer_without_allowed_ips(self): uuid: "{}" name: "wg0" passthrough: - connection.interface-name: "wg0" + connection: + interface-name: "wg0" '''.format(UUID, UUID)}) def test_vxlan_with_local_and_remote(self): @@ -2158,7 +2216,8 @@ def test_vxlan_with_local_and_remote(self): uuid: "{}" name: "vxlan10" passthrough: - connection.interface-name: "vxlan10" + connection: + interface-name: "vxlan10" '''.format(UUID, UUID)}) def test_simple_vxlan(self): @@ -2185,7 +2244,8 @@ def test_simple_vxlan(self): uuid: "{}" name: "vxlan10" passthrough: - connection.interface-name: "vxlan10" + connection: + interface-name: "vxlan10" '''.format(UUID, UUID)}) def test_invalid_tunnel_mode(self): @@ -2238,7 +2298,8 @@ def test_keyfile_wifi_random_cloned_mac_address(self): uuid: "{}" name: "myid with spaces" passthrough: - ipv4.dns-search: "" + ipv4: + dns-search: "" networkmanager: uuid: "{}" name: "myid with spaces" @@ -2271,7 +2332,8 @@ def test_keyfile_ethernet_random_cloned_mac_address(self): uuid: "{}" name: "myid with spaces" passthrough: - ipv4.dns-search: "" + ipv4: + dns-search: "" '''.format(UUID, UUID)}) def test_veth_pair(self): @@ -2297,7 +2359,8 @@ def test_veth_pair(self): uuid: "{}" name: "veth-peer1" passthrough: - connection.interface-name: "veth-peer1" + connection: + interface-name: "veth-peer1" '''.format(UUID, UUID)}) def test_veth_without_peer(self): @@ -2337,7 +2400,8 @@ def test_vrf_basic(self): uuid: "{}" name: "vrf0" passthrough: - connection.interface-name: "vrf0" + connection: + interface-name: "vrf0" '''.format(UUID, UUID)}) def test_vrf_without_table_should_fail(self): @@ -2422,11 +2486,13 @@ def test_nameserver_with_DoT_lp2055148(self): uuid: "{}" name: "ethernet-eth123" passthrough: - ethernet._: "" - ipv4.dns: "8.8.8.8;1.1.1.1#lxd;192.168.0.1#domain.local;" - ipv6.addr-gen-mode: "default" - ipv6.ip6-privacy: "-1" - proxy._: "" + proxy: {{}} + ipv4: + dns: "8.8.8.8;1.1.1.1#lxd;192.168.0.1#domain.local;" + ethernet: {{}} + ipv6: + addr-gen-mode: "default" + ip6-privacy: "-1" '''.format(UUID, UUID)}) def test_ipv4_route_metric_is_overriden_when_dhcp4_is_disabled(self): @@ -2461,12 +2527,15 @@ def test_ipv4_route_metric_is_overriden_when_dhcp4_is_disabled(self): uuid: "{}" name: "dummy-123" passthrough: - connection.interface-name: "dummy123" - ipv4.method: "manual" - ipv4.address1: "100.85.0.1/24,100.85.0.1" - ipv6.addr-gen-mode: "default" - dummy._: "" - proxy._: "" + connection: + interface-name: "dummy123" + proxy: {{}} + ipv4: + method: "manual" + address1: "100.85.0.1/24,100.85.0.1" + ipv6: + addr-gen-mode: "default" + dummy: {{}} '''.format(UUID, UUID)}) def test_ipv6_route_metric_is_overriden_when_dhcp6_is_disabled(self): @@ -2501,12 +2570,15 @@ def test_ipv6_route_metric_is_overriden_when_dhcp6_is_disabled(self): uuid: "{}" name: "dummy-123" passthrough: - connection.interface-name: "dummy123" - ipv4.method: "disabled" - ipv6.method: "manual" - ipv6.address1: "fdeb:446c:912d:8da::/64,fdeb:446c:912d:8da::1" - ipv6.addr-gen-mode: "default" - ipv6.ip6-privacy: "-1" - dummy._: "" - proxy._: "" + connection: + interface-name: "dummy123" + proxy: {{}} + ipv4: + method: "disabled" + ipv6: + addr-gen-mode: "default" + method: "manual" + address1: "fdeb:446c:912d:8da::/64,fdeb:446c:912d:8da::1" + ip6-privacy: "-1" + dummy: {{}} '''.format(UUID, UUID)}) From 23cd665620e75432ef68f3d920883d81dd91ba9a Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Tue, 1 Oct 2024 11:23:20 +0100 Subject: [PATCH 3/8] config_fuzzer: run generate -i against the entire dataset Now that the datalist was replaced with hash tables, run the generator with --ignore-errors against the entire dataset. The datalist was leaking memory and causing ASAN to crash the test. Also limit the value of path-cost to avoid triggering a g_assert(v < G_MACUINT). --- tests/config_fuzzer/runner.sh | 37 ++++++++++++-------------- tests/config_fuzzer/schemas/bridges.js | 6 +++++ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/tests/config_fuzzer/runner.sh b/tests/config_fuzzer/runner.sh index e5b9ff15b..e396fa493 100644 --- a/tests/config_fuzzer/runner.sh +++ b/tests/config_fuzzer/runner.sh @@ -121,29 +121,26 @@ echo "$(date) - Done" echo "$(date) - Running netplan generate -i" -for yaml in ${FAKEDATADIR}/*.yaml -do - rm -rf fakeroot3 - mkdir -p fakeroot3/etc/netplan - cp ${yaml} fakeroot3/etc/netplan/ +# Run the generator against the entire dataset - OUTPUT=$(${NETPLAN_GENERATE_PATH} --root-dir fakeroot3 -i 2>&1) - code=$? - if [ $code -eq 139 ] || [ $code -eq 245 ] || [ $code -eq 133 ] - then - echo "GENERATE --ignore-errors CRASHED" - cat ${yaml} - error=1 - fi +rm -rf fakeroot3 +mkdir -p fakeroot3/etc/ +mv ${FAKEDATADIR} fakeroot3/etc/netplan - if grep 'detected memory leaks' <<< "$OUTPUT" > /dev/null - then - echo "GENERATE --ignore-errors MEMORY LEAK DETECTED" - cat ${yaml} - error=1 - fi +OUTPUT=$(${NETPLAN_GENERATE_PATH} --root-dir fakeroot3 -i 2>&1) +code=$? +# code 134 happens when a g_assert() is triggered +if [ $code -eq 139 ] || [ $code -eq 245 ] || [ $code -eq 133 ] || [ $code -eq 134 ] +then + echo "GENERATE --ignore-errors CRASHED" + error=1 +fi -done +if grep 'detected memory leaks' <<< "$OUTPUT" > /dev/null +then + echo "GENERATE --ignore-errors MEMORY LEAK DETECTED" + error=1 +fi echo "$(date) - Done" diff --git a/tests/config_fuzzer/schemas/bridges.js b/tests/config_fuzzer/schemas/bridges.js index e30054f04..6170aa9a0 100644 --- a/tests/config_fuzzer/schemas/bridges.js +++ b/tests/config_fuzzer/schemas/bridges.js @@ -124,12 +124,18 @@ const bridges_schema = { properties: { eth0: { type: "integer", + minimum: 0, + maximum: 4000000000 }, eth1: { type: "integer", + minimum: 0, + maximum: 4000000000 }, eth2: { type: "integer", + minimum: 0, + maximum: 4000000000 }, } }, From f3762c960cc8aa2f0e8418257e24d4eccea52003 Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Tue, 1 Oct 2024 14:58:54 +0100 Subject: [PATCH 4/8] tests: update config_fuzzer with new passthrough format Now it will generate a mix of old and new formats in the passthrough section. --- tests/config_fuzzer/schemas/common.js | 5 +++++ tests/config_fuzzer/schemas/nm-devices.js | 26 ++--------------------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/tests/config_fuzzer/schemas/common.js b/tests/config_fuzzer/schemas/common.js index 9ae0ebbfa..82ea45217 100644 --- a/tests/config_fuzzer/schemas/common.js +++ b/tests/config_fuzzer/schemas/common.js @@ -210,6 +210,11 @@ export const networkmanager_settings = { type: "string" } }, + patternProperties: { + "[azAZ09-]{1,10}": { + type: "object", + } + } } }, required: ["passthrough"] diff --git a/tests/config_fuzzer/schemas/nm-devices.js b/tests/config_fuzzer/schemas/nm-devices.js index 0e2ef9cd7..059660c36 100644 --- a/tests/config_fuzzer/schemas/nm-devices.js +++ b/tests/config_fuzzer/schemas/nm-devices.js @@ -1,4 +1,4 @@ -import common_properties, { minMaxProperties } from "./common.js"; +import { minMaxProperties, networkmanager_settings } from "./common.js"; const nmdevices_schema = { type: "object", @@ -34,29 +34,7 @@ const nmdevices_schema = { type: "string", enum: ["NetworkManager"] }, - networkmanager: { - type: "object", - additionalProperties: false, - properties: { - uuid: { - type: "string" - }, - name: { - type: "string" - }, - passthrough: { - type: "object", - additionalProperties: true, - properties: { - "connection.type": { - type: "string" - } - }, - required: ["connection.type"] - } - }, - required: ["passthrough"] - }, + ...networkmanager_settings }, required: ["networkmanager"] } From f94a59f87947990873e9d0489a4f77a92470ceca Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Tue, 1 Oct 2024 15:00:18 +0100 Subject: [PATCH 5/8] nm: fix crashes when errors are ignored When --ignore-errors is used, some netdefs might arrive at the NM config writers in a bad state. In such cases we just skip them. Found with config_fuzzer. --- src/nm.c | 26 +++++++++++++++++++---- tests/generator/test_passthrough.py | 33 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/nm.c b/src/nm.c index 108af70fe..daa4af5e5 100644 --- a/src/nm.c +++ b/src/nm.c @@ -100,7 +100,10 @@ type_str(const NetplanNetDefinition* def) g_assert(def->backend_settings.passthrough != NULL); GHashTable *passthrough = def->backend_settings.passthrough; GHashTable* connection = g_hash_table_lookup(passthrough, "connection"); - return g_hash_table_lookup(connection, "type"); + if (connection) { + return g_hash_table_lookup(connection, "type"); + } + return NULL; // LCOV_EXCL_START default: g_assert_not_reached(); @@ -634,6 +637,12 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, else g_assert(ap == NULL); + nm_type = type_str(def); + if (def->type == NETPLAN_DEF_TYPE_NM && nm_type == NULL) { + g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_UNSUPPORTED, "ERROR: %s: NetworkManager connection type undefined\n", def->id); + return FALSE; + } + if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) { g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id); return TRUE; @@ -653,7 +662,6 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, g_key_file_set_string(kf, "connection", "id", nd_nm_id); } - nm_type = type_str(def); if (nm_type && def->type != NETPLAN_DEF_TYPE_NM) g_key_file_set_string(kf, "connection", "type", nm_type); @@ -1082,11 +1090,21 @@ netplan_state_finish_nm_write( GString *tmp = NULL; guint unmanaged = nd->backend == NETPLAN_BACKEND_NM ? 0 : 1; + if (nd->type == NETPLAN_DEF_TYPE_NM_PLACEHOLDER_ || nd->backend == NETPLAN_BACKEND_OVS) { + iter = iter->next; + continue; + } + + nm_type = type_str(nd); + if (nd->type == NETPLAN_DEF_TYPE_NM && nm_type == NULL) { + /* Will happen when errors are ignored */ + iter = iter->next; + continue; + } + g_autofree char* netdef_id = _netplan_scrub_string(nd->id); /* Special case: manage or ignore any device of given type on empty "match: {}" stanza */ if (nd->has_match && !nd->match.driver && !nd->match.mac && !nd->match.original_name) { - nm_type = type_str(nd); - g_assert(nm_type != NULL); g_string_append_printf(nm_conf, "[device-netplan.%s.%s]\nmatch-device=type:%s\n" "managed=%d\n\n", netplan_def_type_name(nd->type), netdef_id, nm_type, !unmanaged); diff --git a/tests/generator/test_passthrough.py b/tests/generator/test_passthrough.py index 91e97b604..77df8d7a2 100644 --- a/tests/generator/test_passthrough.py +++ b/tests/generator/test_passthrough.py @@ -137,6 +137,39 @@ def test_passthrough_basic_mapping_with_duplication(self): match-device=type:ethernet managed=1\n\n''') + def test_passthrough_basic_mapping_no_type_ignore_error(self): + out = self.generate('''network: + version: 2 + nm-devices: + NM-87749f1d-334f-40b2-98d4-55db58965f5f: + renderer: NetworkManager + match: {} + networkmanager: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + name: some NM id + passthrough: + connection: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + permissions: ""''', skip_generated_yaml_validation=True, ignore_errors=True) + + self.assertIn('network type \'nm-devices:\' needs to provide a \'connection.type\'', out) + + def test_passthrough_basic_mapping_no_connection_ignore_error(self): + out = self.generate('''network: + version: 2 + nm-devices: + NM-87749f1d-334f-40b2-98d4-55db58965f5f: + renderer: NetworkManager + match: {} + networkmanager: + uuid: 87749f1d-334f-40b2-98d4-55db58965f5f + name: some NM id + passthrough: + a: + b: c''', skip_generated_yaml_validation=True, ignore_errors=True) + + self.assertIn('network type \'nm-devices:\' needs to provide a \'connection.type\'', out) + def test_passthrough_wifi(self): self.generate('''network: version: 2 From b954585c0b45305711c7f1dc75aa4064e2104058 Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Thu, 7 Nov 2024 16:28:42 +0000 Subject: [PATCH 6/8] parse: fix uninitialized variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the build on Fedora 41: In file included from /usr/include/glib-2.0/glib.h:117, from ../src/parse.c:25: In function ‘g_autoptr_cleanup_generic_gfree’, inlined from ‘get_ip_family’ at ../src/parse.c:1918:22: /usr/include/glib-2.0/glib/glib-autocleanups.h:32:3: error: ‘ip_str’ may be used uninitialized [-Werror=maybe-uninitialized] 32 | g_free (*pp); | ^~~~~~~~~~~~ ../src/parse.c: In function ‘get_ip_family’: ../src/parse.c:1918:22: note: ‘ip_str’ was declared here 1918 | g_autofree char *ip_str; | ^~~~~~ cc1: all warnings being treated as errors --- src/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse.c b/src/parse.c index ed5a038e6..eaa259192 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1927,7 +1927,7 @@ handle_vxlan_tristate(NetplanParser* npp, yaml_node_t* node, const void* data, G STATIC int get_ip_family(const char* address) { - g_autofree char *ip_str; + g_autofree char *ip_str = NULL; char *prefix_len; ip_str = g_strdup(address); From 8addc9a7cb0ac3777391b371dcca82c52c1d5e5e Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Fri, 8 Nov 2024 10:34:55 +0000 Subject: [PATCH 7/8] docs: update with new passthrough format --- doc/netplan-everywhere.md | 44 ++++++++++++++++++++++----------------- doc/netplan-yaml.md | 20 ++++++++++-------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/doc/netplan-everywhere.md b/doc/netplan-everywhere.md index 8d2c353f0..6f59c261a 100644 --- a/doc/netplan-everywhere.md +++ b/doc/netplan-everywhere.md @@ -101,12 +101,14 @@ network: uuid: "0f7a33ac-512e-4c03-b088-4db00fe3292e" name: "Ethernet connection 1" passthrough: - ethernet._: "" - ipv4.ignore-auto-dns: "true" - ipv6.addr-gen-mode: "default" - ipv6.method: "disabled" - ipv6.ip6-privacy: "-1" - proxy._: "" + ethernet: {} + ipv4: + ignore-auto-dns: "true" + ipv6: + addr-gen-mode: "default" + method: "disabled" + ip6-privacy: "-1" + proxy: {} ``` All the configuration under the `passthrough` mapping is added to @@ -126,17 +128,21 @@ network: uuid: "db5f0f67-1f4c-4d59-8ab8-3d278389cf87" name: "myvpnconnection" passthrough: - connection.type: "vpn" - vpn.ca: "path to ca.crt" - vpn.cert: "path to client.crt" - vpn.cipher: "AES-256-GCM" - vpn.connection-type: "tls" - vpn.dev: "tun" - vpn.key: "path to client.key" - vpn.remote: "1.2.3.4:1194" - vpn.service-type: "org.freedesktop.NetworkManager.openvpn" - ipv4.method: "auto" - ipv6.addr-gen-mode: "default" - ipv6.method: "auto" - proxy._: "" + connection: + type: "vpn" + vpn: + ca: "path to ca.crt" + cert: "path to client.crt" + cipher: "AES-256-GCM" + connection-type: "tls" + dev: "tun" + key: "path to client.key" + remote: "1.2.3.4:1194" + service-type: "org.freedesktop.NetworkManager.openvpn" + ipv4: + method: "auto" + ipv6: + addr-gen-mode: "default" + method: "auto" + proxy: {} ``` diff --git a/doc/netplan-yaml.md b/doc/netplan-yaml.md index 2857e7eab..668065be3 100644 --- a/doc/netplan-yaml.md +++ b/doc/netplan-yaml.md @@ -2146,15 +2146,17 @@ network: uuid: "db5f0f67-1f4c-4d59-8ab8-3d278389cf87" name: "myvpnconnection" passthrough: - connection.type: "vpn" - vpn.ca: "path to ca.crt" - vpn.cert: "path to client.crt" - vpn.cipher: "AES-256-GCM" - vpn.connection-type: "tls" - vpn.dev: "tun" - vpn.key: "path to client.key" - vpn.remote: "1.2.3.4:1194" - vpn.service-type: "org.freedesktop.NetworkManager.openvpn" + connection: + type: "vpn" + vpn: + ca: "path to ca.crt" + cert: "path to client.crt" + cipher: "AES-256-GCM" + connection-type: "tls" + dev: "tun" + key: "path to client.key" + remote: "1.2.3.4:1194" + service-type: "org.freedesktop.NetworkManager.openvpn" ``` ## Back end-specific configuration parameters From a381373520e1d630fc88b06c988116fdaebc24fb Mon Sep 17 00:00:00 2001 From: Danilo Egea Gondolfo Date: Fri, 8 Nov 2024 12:05:21 +0000 Subject: [PATCH 8/8] ci/rpmbuild: install gdb The RPM build started to fail on Fedora 41: find-debuginfo: starting Extracting debug info from 4 files gdb-add-index: Failed to find a useable GDB binary --- .github/workflows/rpmbuild.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rpmbuild.yml b/.github/workflows/rpmbuild.yml index b8d80df6e..9e3d407b2 100644 --- a/.github/workflows/rpmbuild.yml +++ b/.github/workflows/rpmbuild.yml @@ -31,6 +31,7 @@ jobs: dnf -y install epel-release || true dnf config-manager --set-enabled crb || true # Meson/CMocka on EL9 dnf -y install centos-release-nfv-openvswitch || true # OVS on EL9 + dnf -y install gdb dnf -y builddep rpm/netplan.spec adduser test chown -R test:test .