The number of authentication retires is useful also for passwords aside
802-1x settings. For example, src/devices/wifi/nm-device-wifi.c also has
a retry counter and uses a hard-coded value of 3.
Move the setting, so that it can be used in general. Although it is still
not implemented for other settings.
This is an API and ABI break.
Normalizing can be complicated, as settings depend on each other and possibly
conflict.
That is, because verify() must exactly anticipate whether normalization will
succeed and how the result will look like. That is because we only want to
modify the connection, if we are sure that the result will verify.
Hence, verify() and normalize() are strongly related. The implementation
should not be spread out between NMSettingOvsInterface:verify(),
NMSettingOvsPatch:verify() and _normalize_ovs_interface_type().
Also, add some unit-tests.
teamd adds the "tx_hash" property for "lacp" and "loadbalance" runners
when not present. Do the same so that our original configuration
matches with the one reported by teamd.
https://bugzilla.redhat.com/show_bug.cgi?id=1497333
We often want to cascade hashing, meaning, to combine the
outcome of various hash functions in a larger hash.
Instead of having each hash function return a guint hash value,
accept a hash state argument. This saves the overhead of initializing
and completing the intermediate hash states.
It also avoids loosing entropy when we reduce the larger hash state
into the intermediate guint hash value.
By using a macro, we don't cast all the types to guint. Instead,
we use their native types directly. Hence, we don't need
nm_hash_update_uint64() nor nm_hash_update_ptr().
Also, for types smaller then guint like char, we save hashing
the all zero bytes.
siphash24() is wildly used by projects nowadays.
It's certainly slower then our djb hashing that we used before.
But quite likely it's fast enough for us, given how wildly it is
used. I think it would be hard to profile NetworkManager to show
that the performance of hash tables is the issue, be it with
djb or siphash24.
Certainly with siphash24() it's much harder to exploit the hashing
algorithm to cause worst case hash operations (provided that the
seed is kept private). Does this better resistance against a denial
of service matter for us? Probably not, but let's better be safe then
sorry.
Note that systemd's implementation uses a different seed for each hash
table (at least, after the hash table grows to a certain size).
We don't do that and use only one global seed.
The privious NM_HASH_* macros directly operated on a guint value
and were thus close to the actual implementation.
Replace them by adding a NMHashState struct and accessors to
update the hash state. This hides the implementation better
and would allow us to carry more state. For example, we could
switch to siphash24() transparently.
For now, we still do a form basically djb2 hashing, albeit with
differing start seed.
Also add nm_hash_str() and nm_str_hash():
- nm_hash_str() is our own string hashing implementation
- nm_str_hash() is our own string implementation, but with a
GHashFunc signature, suitable to pass it to g_hash_table_new().
Also, it has this name in order to remind you of g_str_hash(),
which it is replacing.
We added "ipv4.route-table-sync" and "ipv6.route-table-sync" to not change
behavior for users that configured policy routing outside of NetworkManager,
for example, via a dispatcher script. Users had to explicitly opt-in
for NetworkManager to fully manage all routing tables.
These settings were awkward. Replace them with new settings "ipv4.route-table"
and "ipv6.route-table". Note that this commit breaks API/ABI on the unstable
development branch by removing recently added API.
As before, a connection will have no route-table set by default. This
has the meaning that policy-routing is not enabled and only the main table
will be fully synced. Once the user sets a table, we recognize that and
NetworkManager manages all routing tables.
The new route-table setting has other important uses: analog to
"ipv4.route-metric", it is the default that applies to all routes.
Currently it only works for static routes, not DHCP, SLAAC,
default-route, etc. That will be implemented later.
For static routes, each route still can explicitly set a table, and
overwrite the per-connection setting in "ipv4.route-table" and
"ipv6.route-table".
We already have nm_strquote_a(). That is useful, but uses alloca(), hence it
is ill suited to be called from a macro, inside a loop, or from a function
that should be inlined.
Instead, add nm_strquote() that has the same purpose but writes to a provided
string buffer.
GArray's and GPtrArray's plen argument is unsigned. The index variable
to iterate the list, should not have a smaller range (or different data type).
Also, assert against negative idx argument.
A replacement for g_strsplit_set(). While g_strsplit_set()
does (n+1) malloc and n slice allocations, this needs
roughtly (O(log(n))) mallocs.
Another difference from g_strsplit_set() is that this function
treats multiple delimiters as one (and thus never returns empty
words). While I can see that sometimes you may want to keep empty
words (like parsing a CSV file and preserve empty cells), we usually
use this function for splitting user input. In such case, we want
to treat multiple delimiters as one.
Add a stable, recursive merge sort for CList.
This could be improved by doing an iterative implementation.
The recursive implementation's stack depth is not an issue,
as it is bound by O(ln(n)). But an iterative implementation
would safe the overhead of O(n*log(n)) function calls and be
potentially faster.
And get rid of the unused obj_full_equality_allows_different_class.
It's hard to grasp how to implement different object types that can compare
despite having different klasses. The idea was, that stack allocated
objects (used as lookup needles), are some small lightweight objects,
that still compare equal to the full instance. But it's unused. Drop it.
Implement the reference counting of NMPObject as part of
NMDedupMultiObj and get rid of NMDedupMultiBox.
With this change, the NMPObject is aware in which NMDedupMultiIndex
instance it is tracked.
- this saves an additional GSlice allocation for the NMDedupMultiBox.
- it is immediately known, whether an NMPObject is tracked by a
certain NMDedupMultiIndex or not. This saves an additional hash
lookup.
- previously, when all idx-types cease to reference an NMDedupMultiObj
instance, it was removed. Now, a tracked objects stays in the
NMDedupMultiIndex until it's last reference is deleted. This possibly
extends the lifetime of the object and we may reuse it better.
- it is no longer possible to add one object to more then one
NMDedupMultiIndex instance. As we anyway want to have only one
instance to deduplicate the objects, this is fine.
- the ref-counting implementation is now part of NMDedupMultiObj.
Previously, NMDedupMultiIndex could also track objects that were
not ref-counted. Hoever, the object anyway *must* implement the
NMDedupMultiObj API, so this flexibility is unneeded and was not
used.
- a downside is, that NMPObject grows by one pointer size, even if
it isn't tracked in the NMDedupMultiIndex. But we really want to
put all objects into the index for sharing and deduplication. So
this downside should be acceptable. Still, code like
nmp_object_stackinit*() needs to handle a larger object.
Add the NMDedupMultiIndex cache. It basically tracks
objects as doubly linked list. With the addition that
each object and the list head is indexed by a hash table.
Also, it supports tracking multiple distinct lists,
all indexed by the idx-type instance.
It also deduplicates the tracked objects and shares them.
- the objects that can be put into the cache must be immutable
and ref-counted. That is, the cache will deduplicate them
and share the reference. Also, as these objects are immutable
and ref-counted, it is safe that users outside the cache
own them too (as long as they keep them immutable and manage
their reference properly).
The deduplication uses obj_id_hash_func() and obj_id_equal_func().
These functions must cover *every* aspect of the objects when
comparing equality. For example nm_platform_ip4_route_cmp()
would be a function that qualifies as obj_id_equal_func().
The cache creates references to the objects as needed and
gives them back. This happens via obj_get_ref() and
obj_put_ref(). Note that obj_get_ref() is free to create
a new object, for example to convert a stack-allocated object
to a (ref-counted) heap allocated one.
The deduplication process creates NMDedupIndexBox instances
which are the ref-counted entity. In principle, the objects
themself don't need to be ref-counted as that is handled by
the boxing instance.
- The cache doesn't only do deduplication. It is a multi-index,
meaning, callers add objects using a index handle NMDedupMultiIdxType.
The NMDedupMultiIdxType instance is the access handle to lookup
the list and objects inside the cache. Note that the idx-type
instance may partition the objects in distinct lists.
For all operations there are cross-references and hash table lookups.
Hence, every operation of this data structure is O(1) and the memory
overhead for an index tracking an object is constant.
The cache preserves ordering (due to linked list) and exposes the list
as public API. This allows users to iterate the list without any
additional copying of elements.
Using plain numbers make it cumbersome to grep for
setting types by priority.
The only downside is, that with the enum values it
is no longer obvious which value has higher or lower
priority.
Also, introduce NM_SETTING_PRIORITY_INVALID. This is what
_nm_setting_type_get_base_type_priority() returns. For the moment
it still has the same numerical value 0 as before. Later, that
shall be distinct from NM_SETTING_PRIORITY_CONNECTION.
In an ideal world, we should not validate connections containing
options not valid for the current bond mode. However adding such
restriction now means that during an upgrade to the new NM version
some connections that were valid before become invalid, possibly
disrupting connectivity.
Instead, consider invalid options as a normalizable error and remove
them during normalization.
Converting the setting to a "canonical" form without invalid options
is important for the connection matching logic, where such invalid
options can cause false mismatches.
When the two base settings are present, use one of higher priority.
This will pick the "bridge" setting when both "bridge" and "bluetooth" are
present for a Bluetooth NAP connection.
We will need multiple base settings for Bluetooth NAP servers: bluetooth and
bridge. We want to identify the device as "bluetooth" to the user, but leave
the Bridge factory handle it.
The "connection.type" is somewhat redundant -- let's keep it for what the user
sees. And identify the actual base setting to pick the right factory by the
actually present setting.
Returning TRUE for zero makes no sense. Obviously, zero is not a power
of two.
Also, the function is used to check whether a number has only one bit
(flag) set, so, an alternative name would be "has-one-bit-set", which
also should return FALSE for zero. All callers didn't really care for
the previous meaning "has-at-most-one-bit-set".
This also avoids the issue of checking (x >= 0), which causes
-Wtype-limits warnings for unsigned types. Which was avoided
by doing (x == 0 || x > 0), which caused -Wlogical-op warning,
which then was avoided (x == 0 || (x > 0 && 1)). Just don't.
We recently added -Wlogical-op in our build process
(commit #41e7fca59762dc928c9d67b555b1409c3477b2b0).
Seems that old versions of gcc (4.8.x) will hit that warning with our
implementation of our "nm_utils_is_power_of_two" and
"test_nm_utils_is_power_of_two_do" macros.
Fool it just adding an always TRUE check.
Use C-style backslash escaping to sanitize non-UTF-8 strings.
The functions are compatible with glib's g_strcompress() and
g_strescape().
The difference is only that g_strescape() escapes all non-printable,
non ASCII character as well, while nm_utils_str_utf8safe_escape()
-- depending on the flags -- preserves valid UTF-8 sequence except
backslash.
The flags allow to optionally escape ASCII control characters and
all non-ASCII (valid UTF-8) characters. But the option to preserve
valid UTF-8 (non-ASCII) characters verbatim, is what distinguishes
from g_strescape().
vpn.data, bond.options, and user.data encode their values directly as
keys in keyfile. However, keys for GKeyFile may not contain characters
like '='.
We need to escape such special characters, otherwise an assertion
is hit on the server:
$ nmcli connection modify "$VPN_NAME" +vpn.data 'aa[=value'
Another example of encountering the assertion is when setting user-data key
with an invalid character "my.this=key=is=causes=a=crash".
I used to use g_strv_length ((char **) p) instead, but that feels
ugly because it g_strv_length() is not designed to operate on
arbitrary pointer arrays.
It is not uncommon that a flags type has also the value 0 mapped,
for example to "unknown" or "none".
In that case, we should not return an empty string, but instead
that zero value.
Also, flags actually have an unsigned type. That isn't a real
problem to cast it to a signed int. But be more careful about
it and use unsigned while handling unsigned values and only
cast to int once.
This adds definition of a set of known route option attributes to
libnm-core and helper functions.
nm_ip_route_attribute_validate() performs the validation of the
attribute type and, in case of a formatted string attribute, of its
content.
nm_ip_route_get_variant_attribute_spec() returns the attribute format
specifier to be passed to nm_utils_parse_variant_attributes(). Since
at the moment NMIPRoute is the only user of NMVariantAttributeSpec and
the type is opaque to users of the library, the struct is extended to
carry some other data useful for validation.
- for nm_utils_enum_to_str(), whenever encounter a numeric value
that has no expression as enum/flag, encode the value numerically.
For enums, encode it as decimal. For flags, encode it as hexadecimal
(with 0x prefix).
Also check that an existing value_nick cannot be wrongly interpreted
as a integer, and if they would, encode them instead as integers only.
- Likewise, in nm_utils_enum_from_str() accept numerical values
and for nm_utils_enum_get_values() return enum nicks that look
like numeric values in their numeric form only.
- In nm_utils_enum_from_str(), don't use g_strsplit(), but clone the
string only once and manipulate it inplace.
- Accept '\n' and '\r' as additional delimiters for flags.
- For consistency, also return an err_token for enum types. If the caller
doesn't care about that, he should simply not pass the out-argument.
While technically it's already possible to implement a fail-over
mechanism using multiple connections (for example, defining a higher
priority DHCP connection with short DHCP timeout and a lower priority
one with static address), in practice this doesn't work well as we try
to autoactivate each connection 4 times before switching to the next
one.
Introduce a connection.autoconnect-retries property that can be used
to change the number of retries. The special value 0 means infinite
and can be used to try the connection forever. A -1 value means the
global configured default, which is equal to 4 unless overridden.
https://bugzilla.gnome.org/show_bug.cgi?id=763524
g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR().
As glib's version of _G_BOOLEAN_EXPR() uses a local variable
_g_boolean_var_, we cannot nest a G_LIKELY() inside a G_LIKELY(),
or inside a g_assert(), or a g_assert() inside a g_assert().
Workaround that, by redefining the macro.
I already encountered this problem before, when having a nm_assert()
inside a ({...}) block, inside a g_assert(). Then I just avoided that
combination, but this situation is quite easy to encounter.
Since we possibly already link against libjansson, we can also expose some
helper utils which allows nmcli to do basic validation of JSON without
requiring to duplicate the effort of using libjansson.
Also, tighten up the cecks to ensure that we have a JSON object at hand.
We are really interested in that and not of arrays or literals.
crypto_verify_private_key_data() must try to decrypt the key only when
a password is supplied.
Previously the decrypt test always passed because we detected an
unsupported cipher and faked success. Now since version 3.5.4 gnutls
supports PBES1-DES-CBC-MD5 and the key is actually decrypted when a
password is supplied.
Also, don't assert that a wrong password works because we're now able
to actually verify it (only with recent gnutls).
https://bugzilla.gnome.org/show_bug.cgi?id=771623