They are basically the same, with a minor difference where the @filename
argument determines whether to write a new file or do an update.
Also, rename them, to give them a nms_* prefix in the header file.
As writing a connection to disk might modify it, we re-read
it back and use what we actually found on disk.
For example, if you have a connection with ipv6.method=ignore,
ifcfg-rh writer will not persist the ipv6.route-metric. That
is likely a bug in the writer. Before this patch, changing
the route metric would seemingly succeed, but on the next reload
from this, the changes are lost.
We should fix such bugs. Regardless, it's better to pick up
what we wrote to disk, instead of later.
Previously, we would first call replace_settings(), followed by
commit_changes(). There are two problems with that:
- commit_changes() might fail easily, for example if the settings
plugin cannot handle the connection. In that case, we fail the operation,
but still we already replaced the settings in memory. We should
first write to disk, and only when that succeeded, replace our
settings.
Also, note that replace_settings() cannot really fail at that
point, because we already validate the setting previously
(everything else would be a bug).
- commit_changes() might modify the connection while writing it.
We re-read it and replace the settings. If we already replaced
it before, we replcace the settings twice -- needlessly.
During write, it can regularly happen that the connection gets modified.
For example, keyfile never writes blobs as-is, it always writes the
blob to an external file, and replaces the certificate property with
a path.
Other reasons could be just bugs, where the reader and writer are not doing
a proper round trip (these cases should be fixed).
Refactor commit_changes(), to return the re-read connection to
the settings-connection class, and handle replacing the settings
there.
Also, prepare for another change. Sometimes we first call replace_settings()
followed by commit_changes(). It would be better to instead call commit_changes()
first, and only on success proceed with replace_settings(). Hence, commit_changes()
gets a new argument new_connection, that can be used to write another
connection to disk.
Don't delegate so much to the virtual function commit_changes().
Calling the callback is not the task of the virtual function,
because every implementation must do that.
There are some minor changes in behavior for ifnet, where we now
first setup the monitors and reload the parsers, before invoking
the callback.
The virtual function replace_and_commit() had only one implementation: ifcfg-rh.
Refactor the code, to delegate less. That is, the main part of
replace-and-commit is not delegated to a virtual function.
Now, the virtual function is only a pre-check hook, so that
the ifcfg-rh implementation can abort the function.
There are no functional changes.
Also, need to avoid danling pointers in clear_monitor().
This was not really a problem, because we would always call
cancel() before setup(). Still, it's fragile.
Since commit 5c299454b4 we can configure
multiple default-routes.
That is especially useful with IPv6 to configure multiple routers.
It will also be useful, once we allow configuring manual default-routes,
like regular static routes.
However the problem is, that the default-route for the manual gateway
and the gateway from DHCP both get the same metric. So it's undefined
which route is used. To avoid that problem, and to restore previous
behavior, don't accept any default-routes if a gateway is set.
Fixes: 5c299454b4
After commit 5c299454b4 ("core: rework tracking of
gateway/default-route in ip-config") NM set a default route for VPNs
only based on the "never-default" option reported by the plugin. It
should also consider the connection setting.
Fixes: 5c299454b4https://bugzilla.redhat.com/show_bug.cgi?id=1505886
Setting the MTU might fail when the underlying device's MTU
is not set.
Detect that case, and log a better warning message.
Unfortunately, it's tricky to detect whether this is a complete
failure, or whether we will later try again to change the MTU.
So, we log a failure, altough later we might fix it. It would
be better not to warn about non-errors.
and nm_utils_ip6_property_path(). The API with static buffers
looks a bit nicer. But I think they are dangerous, because
we tend to pass the buffer down several layers of the stack, and
it's not immediately clear, that we don't overwrite the static
buffer again (which we probably did not, but it's hard to verify
that there is no bug there).
Setting the MTU failes under regular conditions, for example when
setting the MTU of a master larger then the MTU of the slaves.
Logging a warning it too alarming.
Kernel does not allow setting the MTU of a VLAN larger
then the MTU of the underlying device. Hence, we might
initially fail to set a large MTU of the VLAN, but we
have to retry when the MTU of the parent changes.
https://bugzilla.redhat.com/show_bug.cgi?id=1414901
When comparing an unsigned and a signed integer, the signed integer
is promoted to unsigned, resulting in a very large number.
See the checks "nwrote < len - 1", where nwrote might be -1
to indicate failure. The condition would not be TRUE due to
promoting -1 to the max int value.
Hence, sysctl_set() was rather wrong.
In many scenarios, we have no use for the file descriptor
after nm_utils_fd_get_contents(). We just want to read it
and close it.
API wise, it would be nice that the get_contents() function never
closes the passed in fd and it's always responsibility of the caller.
However, that costs an additional dup() syscall that could
be avoided, if we allow the function to (optionally) close
the file descriptor.
The function should not close the input file descriptor; however
fdopen() associates the fd to the new stream so that when the stream
is closed, the fd is too. The result is a double close() and the
second call can in certain cases affect a wrong fd.
Use a duplicate fd for the stream.
Fixes: 1d9bdad1dfhttps://bugzilla.redhat.com/show_bug.cgi?id=1451236
libnm-core limits the rande for GATEWAY_PING_TIMEOUT to 0 to 600.
See commit e86f8354a7, "device: restart
ping process when it exits with an error".
The reader must not pass value out of range to g_object_set().
Clamp and warn.
"nm-dhcp-manager.h" forward declares _nm_dhcp_manager_factories.
We need to make the definition aware of the declaration, so
that the compiler can warn if they differ.
We don't need this extra distinguisher. It makes no sense to ever
compare two routes with a different compare-type.
Also, the number of fields that is hashed already differs between each
compare type. If we have a good hashing algorithm, this already suffices
that the hash value looks largely different.
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.
Replace the usage of g_str_hash() with our own nm_str_hash().
GLib's g_str_hash() uses djb2 hashing function, just like we
do at the moment. The only difference is, that we use a diffrent
seed value.
Note, that we initialize the hash seed with random data (by calling
getrandom() or reading /dev/urandom). That is a change compared to
before.
This change of the hashing function and accessing the random pool
might be undesired for libnm/libnm-core. Hence, the change is not
done there as it possibly changes behavior for public API. Maybe
we should do that later though.
At this point, there isn't much of a change. This patch becomes
interesting, if we decide to use a different hashing algorithm.
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.