Unfortunately, for this we require SetLinkDNSEx() API from v246.
That adds extra complexity.
If the configuration contains no server name, we continue using
SetLinkDNS(). Otherwise, at first we try using SetLinkDNSEx().
We will notice if that method is unsupported, reconfigure with
SetLinkDNS(), and set a flag to not try that again.
- nm_setting_ip_config_add_dns() and nm_setting_ip_config_remove_dns_by_value()
used to assert that the provided input is valid. That is not
documented and highly problematic.
Our parsing code for keyfile, ifcfg-rh and GVariant rightly just call
add. Likewise, nmcli. We cannot reasonably expect them to pre-validate
the input. Why would we anyway?
This is wrong in particular because we usually want the user to be
able to construct invalid settings. That is often necessary, because
whether a value is valid depends on other values. So in general, we
can only validate when all properties are set. We have
nm_connection_verify() for that, and asserting/validating during add
is very wrong. Note that "add" still filters out duplicates, which
may be an inconsistency, but well.
Also, the user could set any bogus value via the NM_SETTING_IP_CONFIG_DNS
property. Those should be allowed to be removed, and the same values
should be allowed to be added via the add method.
- add() does a normalization, presumably so that the values look nice.
Do the same normalization also when using the NM_SETTING_IP_CONFIG_DNS
property setter.
- previously, the setter could also set unnormalized values. As
nm_setting_ip_config_remove_dns_by_value() looked for the normalized
value, you couldn't remove such values anymore. That is fixed now,
by letting the property setter do the same normalization.
- don't allocate a GPtrArray unless we need it. No need for the extra
allocation.
- in the property setter, first set the new value before destroying the
previous GPtrArray. It might not be possible, but it's not clear to me
whether the strv argument from the GValue is always deep-copied or
whether it could contain strings to the DNS property itself.
On D-Bus, the properties "ipv[46].dns" are of type "au" and "aay",
respectively.
Btw, in particular "au" is bad, because we put there a big-endian
number. There is no D-Bus type to represent big endian numbers, so "u"
is bad because it can cause endianess problem when trying to remote
the D-Bus communication to another host (without explicitly
understanding which "u" properties need to swap for endinness).
Anyway. The plain addresses are not enough. We soon will also support
the DNS-over-TLS server name, or maybe a DoT port number. The previous
property was not extensible, so deprecate it and replace it by
"dns-data".
This one is just a list of strings. That is unlike "address-data" or
"route-data", which do a similar thing but are "a{sv}" dictionaries.
Here a string is supposed to be sufficient also for the future. Also,
because in nmcli and keyfile will will simply have a string format for
representing the extra data, not a structure (unlike for routes or
addresses).
The previous could would first check whether the new property is not
set. In almost all cases, the new property is actually set.
We can get away with fewer lookups, by checking for the expected things
first.
We have 4 legacy properties ("ipv[46].addresses", "ipv[46].routes") that
got replaced by newer variants ("ipv[46].address-data", "ipv[46].route-data").
When the client side of libnm (_nm_utils_is_manager_process) serializes
those properties, it must only serialize the newer version. That is so
that the forward/backward compatibility works as intended.
Previously, there was the NM_SETTING_PARAM_LEGACY GObject property flag.
That was fine, but not very clear.
For one, the legacy part of those properties is only about D-Bus. In
particular, they are not deprecated in libnm, keyfile, or nmcli. Thus
the name wasn't very clear.
Also, in the meantime we have more elaborate property meta data, that
goes beyond the meta data of the GObject property.
Move NM_SETTING_PARAM_LEGACY to NMSettInfoProperty.to_dbus_only_in_manager_process.
I think, this is a better name. It's also right at
```
_nm_properties_override_gobj(
properties_override,
g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ROUTES),
NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("a(ayuayu)"),
.to_dbus_fcn = ip6_routes_to_dbus,
.compare_fcn = _nm_setting_ip_config_compare_fcn_routes,
.from_dbus_fcn = ip6_routes_from_dbus, ),
.to_dbus_only_in_manager_process = TRUE,
.dbus_deprecated = TRUE, );
```
that is, directly at the place where we describe how the D-Bus property behaves.
When an authentication attempt fails, NetworkManager re-requests new secrets
from agents before retrying. This is currently decided outside of the NMSetting
objects. With this change the decision if a re-request of new secrets is really
needed is moved down to the NMSetting implementations.
For the case "802.1x authentication with TLS" a certificate with password is
configured and the assumption is, that this can never be wrong and no re-request
is needed.
Clang 15 now (correctly) warns about this:
../src/libnm-core-impl/nm-vpn-plugin-info.c:201:40: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
_nm_vpn_plugin_info_get_default_dir_etc()
^
void
../src/libnm-core-impl/nm-vpn-plugin-info.c:213:40: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
_nm_vpn_plugin_info_get_default_dir_lib()
^
void
../src/libnm-core-impl/nm-vpn-plugin-info.c:226:41: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
_nm_vpn_plugin_info_get_default_dir_user()
^
void
../src/libnm-core-impl/nm-vpn-plugin-info.c:315:29: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
nm_vpn_plugin_info_list_load()
^
void
When you call
nm_uuid_generate_from_strings_strv(uuid_type, type_arg, v1, v2);
you'd probably expect that both values are honored in some way.
However, if v1 happened to be NULL, then previously v2 would be ignored.
Extend nm_uuid_generate_from_strings() to accept also NULL values and
pass on the length. Currently, there are no users of nm_uuid_generate_from_strings(),
so nobody is affected by this change.
Also extend nm_uuid_generate_from_strings_strv() to take a length
argument. It still accepts "-1" to take the input strv as a NULL
terminated array.
If a positive length is provided to nm_uuid_generate_from_strings_strv(),
it hashes the same UUID as the respective NULL terminated array. But of
course only, if there is no NULL inside the array. If there are any
NULLs, a distinct UUID gets generated.
For a long time we have a function like nm_uuid_generate_from_strings().
This was recently reworked and renamed, but it preserved behavior. Preserving
behavior is important for this function, because for the existing users,
we need to keep generating the same UUIDs.
Originally, this function was a variadic function with NULL sentinel.
That means, when you write
nm_uuid_generate_from_strings(uuid_type, type_arg, v1, v2, v3, NULL);
and v2 happens to be NULL, then v3 is ignored. That is most likely not
what the user intended. Maybe they had a bug and v2 should not be NULL.
But nm_uuid_generate_from_strings() should not require that all
arguments are non-NULL and it should not ignore arguments after the
first NULL.
For example, one user works around this via
uuid = nm_uuid_generate_from_strings_old("ibft",
s_hwaddr,
s_vlanid ? "V" : "v",
s_vlanid ? s_vlanid : "",
s_ipaddr ? "A" : "DHCP",
s_ipaddr ? s_ipaddr : "");
which is cumbersome and ugly.
That will be fixed next, by adding a function that doesn't suffer
from this problem. But "this problem" is part of the API of the
function, we cannot just change it. Instead, rename it and all
users, so they can keep doing the same.
New users of course should no longer use the "old" function.
The file "nm-meta-setting-base-impl.c" is shared by "libnm-core-impl" and
"libnmc-setting". For "libnm-core-impl" it uses a efficient lookup from the
gtype. For "libnmc-setting", that class information is not available, so
it did a linear search. Instead, do a binary search.
Tested:
diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c
index 3434c858391f..62c366d2ca42 100644
--- a/src/libnm-core-impl/nm-meta-setting-base-impl.c
+++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c
@@ -821,6 +821,11 @@ nm_meta_setting_infos_by_gtype(GType gtype)
{
const NMMetaSettingInfo *setting_info;
+#if _NM_META_SETTING_BASE_IMPL_LIBNM
+ return _infos_by_gtype_binary_search(gtype);
+#endif
+ nm_assert_not_reached();
+
#if _NM_META_SETTING_BASE_IMPL_LIBNM
setting_info = _infos_by_gtype_from_class(gtype);
#else
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index 85d549eb766c..65fcafd076c9 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -5134,6 +5134,29 @@ main(int argc, char **argv)
{
nmtst_init(&argc, &argv, TRUE);
+ {
+ gs_unref_object NMConnection *con = NULL;
+ guint8 ctr = 0;
+ guint i;
+
+ con = nmtst_create_minimal_connection("test", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+
+ nm_connection_add_setting(con, nm_setting_wired_new());
+
+ nmtst_connection_normalize(con);
+ nmtst_assert_connection_verifies_without_normalization(con);
+
+ for (i = 0; i < 10000000; i++) {
+ ctr += GPOINTER_TO_UINT(nm_connection_get_setting(con, NM_TYPE_SETTING_WIRED));
+ ctr += GPOINTER_TO_UINT(nm_connection_get_setting(con, NM_TYPE_SETTING_CONNECTION));
+ ctr += GPOINTER_TO_UINT(nm_connection_get_setting(con, NM_TYPE_SETTING_PROXY));
+ ctr += GPOINTER_TO_UINT(nm_connection_get_setting(con, NM_TYPE_SETTING_WIREGUARD));
+ ctr += GPOINTER_TO_UINT(nm_connection_get_setting(con, NM_TYPE_SETTING_IP4_CONFIG));
+ }
+
+ return !!ctr;
+ }
+
g_test_add_func("/libnm/test_connection_uuid", test_connection_uuid);
g_test_add_func("/libnm/settings/test_nm_meta_setting_types_by_priority",
Results of `make src/libnm-core-impl/tests/test-setting && libtool --mode=execute perf stat -r 5 -B src/libnm-core-impl/tests/test-setting`:
1) previous linear search: 3,182.81 msec
2) bsearch not inlined: 1,611.19 msec
3) bsearch open-coded: 1,214.45 msec
4) bsearch inlined: 1,167.70 msec
5) lookup from class: 1,147.64 msec
1) previous implementation
2) using nm_array_find_bsearch()
3) manually implementing binary search
4) using nm_array_find_bsearch_inline()
5) only available to libnm-core as it uses internal meta data
Note that for "libnm-core-impl" the implementation was already fast (5), and
it didn't change. It only changed to binary search for "libnmc-setting",
which arguably is a less strong use-case.
The file "nm-meta-setting-base-impl.c" is present twice in our source
tree and compiled twice. Once with internal headers of libnm-core and
once with only public headers.
Consequently, there are two implementations for
nm_meta_setting_infos_by_gtype(), one that can benefit from internal
access to the data structure, and the one for libnmc, which cannot.
Refactor the implementations, in the hope to have it clearer.
The G_TYPE_INSTANCE_GET_CLASS() macro is just one pointer dereference
(self)->g_class, plus additional assertions with debug builds.
As such, it is as fast as it gets. Embed the address family there, and
implement NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY() that way.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1395
NMConnection is an interface, implemented by NMSimpleConnection and
NMRemoteConnection.
For the most part, an NMConnection is only the content of the profile
(the settings). The "path" of the connection refers to the D-Bus path,
and wouldn't really make sense of the NMConnection interface or the
NMSimpleConnection type.
As such, the daemon (which only uses NMConnection and
NMSimpleConnection) never sets the path. Only libnm does.
NMClient uses NMRefString extensively for the D-Bus interface and the
path is already internalized. Take advantage of that. It is very likely,
that we are able to share the path instance in libnm at which point it
makes sense to use NMRefString.
Also, during nm_simple_connection_new_clone(), we can just take another
reference instead of cloning the string.
For new uses of nm_uuid_generate_from_strings() we should generate version5
UUIDs and we should use unique namespace UUID arguments.
The namespace UUID was so far replaced by always passing a special prefix
as first string. It seems nicer to use a namespace instead.
Version3 UUIDs should not be used for new applications.
Hence, nm_uuid_generate_from_strings_v3() is no longer a desirable way to
generate UUIDs, so drop the wrapper.
nm_uuid_generate_from_strings() accepts a uuid_type and type_arg
parameter, so that we can use it to generate version 5 UUIDs.
This is a more flexible variant of nm_uuid_generate_from_strings_v3(),
which will be used to replace it. With the right parameters, the new
function behaves the same as nm_uuid_generate_from_strings_v3().
nm_uuid_generate_from_strings() uses variant3 UUIDs based on MD5.
We shouldn't use that in the future.
We will add a replacement, so rename this function so that the "good"
name is free again. Of course, code that uses this function currently
relies on that the behavior doesn't change. We cannot just drop it
entirely, but will replace it by something that gives the same result.
Rename.
We already redefine those checks to optimize for NMSimpleConnection.
Which, in particular when libnm-core is used by the daemon, is the only
implementation of the NMConnection interface.
Move those to the private header file. No need to keep it private to
"nm-connection.c".
NMConnection is an interface, and as such has no data itself.
In practice, there are only two implementations of this interface,
NMSimpleConnection and NMRemoteConnection. The latter only exists
in libnm, not the daemon.
Thus, lookup of the private data is already optimized for
NMSimpleConnection instances via _nm_simple_connection_private_offset.
Use the same mechanism also for NMSimpleConnection itself.
Have "len" before "elem_size". That is consistent with g_qsort_with_data()
and bsearch(), and is also what I would expect.
Note that the previous commit just renamed the function. If a user
of the new, changed API gets backported to an older branch, we will
get a compilation error and note that the arguments need to be adjusted.
The "nm_utils_" prefix is just too verbose. Drop it.
Also, Posix has a bsearch function. As this function
is similar, rename it.
Note that currently the arguments are provided in differnt
order from bsearch(). That will be partly addressed next.
That is the main reason for the rename. The next commit
will swap the arguments, so do a rename first to get a compilation
error when backporting a patch that uses the changed API.
It is allowed to have a connection with empty connection.slave-type
and a NMSettingBondPort; the property will be set automatically during
normalization if a master is set, otherwise the setting will be removed.
With this change, it becomes possible to remove a port from a bond
from nmcli, turning it into a non-slave connection. Before, this used
to fail with:
$ nmcli connection add type ethernet ifname test con-name test+ connection.master bond0 connection.slave-type bond
$ nmcli connection modify test+ connection.master '' connection.slave-type ''
Error: Failed to modify connection 'test+': connection.slave-type: A connection with a 'bond-port' setting must have the slave-type set to 'bond'
https://bugzilla.redhat.com/show_bug.cgi?id=2126262https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1382
Fixes: 9958510f28 ('bond: add support of queue_id of bond port')
Bond option values are just strings, however, some of them get
validated to be numbers, etc.
We also have effectively boolean values, like "use-carrier". Internally,
this is not validates as a boolean (_nm_utils_ascii_str_to_bool()) but
instead is an integer of either "0" or "1".
Add a helper function_nm_setting_bond_opt_value_as_intbool() to access
and parse such values.
The bond setting does some minimal validation of the options.
At least for those number typed values, it validates that the
string can be interpreted as a number and is within a certain range.
Add nm_assert() checks to our opt_value_u$SIZE() functions, that the
requested option is validated to be in a range which is sufficiently
narrow to be converted to the requested type. If that were not the case,
we would need some special handling (or question whether the option should
be retrieved as this type).
These variants provide additional nm_assert() checks, and are thus
preferable.
Note that we cannot just blindly replace &g_array_index() with
&nm_g_array_index(), because the latter would not allow getting a
pointer at index [arr->len]. That might be a valid (though uncommon)
usecase. The correct replacement of &g_array_index() is thus
nm_g_array_index_p().
I checked the code manually and replaced uses of nm_g_array_index_p()
with &nm_g_array_index(), if that was a safe thing to do. The latter
seems preferable, because it is familar to &g_array_index().
When serializing setting properties to GVariant/D-Bus, we usually
omit values that are set to the default. That is done by libnm(-core),
so it happens both on the daemon and client side. That might be
useful to avoid a large number of properties on D-Bus.
Before changing the default value for "ipv6.addr-gen-mode" ([1]), we
would not serialize the previous default value ("stable-privacy").
Now we would serialize the new default value ("default). This change
causes problems.
Scenario 1: have a profile in the daemon with "ipv6.addr-gen-mode=stable-privacy",
have an older daemon version before [1] and a newer client after [1]. Result:
The daemon exposes the profile on D-Bus without the addr-gen-mode
field (because it's the default). To the client, that is interpreted
differently, as "ipv6.addr-gen-mode=default". This is already somewhat
a problem.
More severe is when modifying the profile, the client would now serialize
the value "default" on D-Bus, which the older daemon rejects as invalid.
That means, you could not modify the profile, unless also resetting
addr-gen-mode to "stable-privacy" or "eui64".
You can imagine other scenarios where either the daemon or the client is
before/after change [1] and the addr-gen-mode is set to either "default"
or "stable-privacy". Depending on what scenario you look, that can either be
good or bad.
Scenario 1 is pretty bad, because it means `dnf upgrade NetworkManager
&& nmcli connection modify ...` will fail (if the daemon was not
restated). So try to fix Scenario 1, by also not serializing the new
default value on D-Bus. Of course, some of the scenarios will get
different problems, by exacerbating one side misunderstanding the actually
set value and interpreting a missing value on D-Bus wrongly. But those
problems are likely less severe.
In case both client and daemon are older/newer than [1], it doesn't
matter either way. The problem happens with different version and is
caused by a change of the default value.
[1] e6a33c04ebhttps://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1372
Add deprecation tags to "subject-match" and "phase2-subject-match"
properties and adjust the documentation slightly.
They've been deprecated since commit 64b76ba906 ('libnm-core: add
domain-suffix-match properties to NMSetting8021x').
The parser is reworked, and this line could be wrongly parsed
because it starts with " * value:" which could be misinterpreted
as a tag. It actually won't be parsed wrongly and is not parsed
wrongly now. Still, avoid this potential ambiguity by breaking
the line differently.