For historic reasons is NMSettingBond implemented differently from other
settings. It uses a strdict, and adds some validation on top of that.
The idea was probably to be able to treat bond options more generically.
But in practice we cannot treat them as opaque values, but need to know,
validate and understand all the options. Thus, this implementation with a
strdict is not nice.
The user can set the GObject property NM_SETTING_BOND_OPTIONS to any
strdict, and the setter performs no validation or normalization. That
is probably good, because g_object_set() cannot return an error to
signalize invalid settings. As often, we have corresponding C API like
nm_setting_bond_add_option() and nm_setting_bond_remove_option(). It
should be possible to get the same result both with the C API and with
the GObject property setting. Since there is already a way to set
certain invalid values, it does not help if the C API tries to prevent
that. That implies, that also add-option does not perform additional
validation and sets whatever the user asks.
Remove all validation from nm_setting_bond_add_option() and
nm_setting_bond_remove_option(). This validation was anyway only very
basic. It was calling nm_setting_bond_validate_option(), which can check
whether the string is (for example) and integer, but it cannot do
validation beyond one option. In most cases, the validation needs to
take into account the bond mode or other options, so validating one
option in isolation is not very useful.
Proper validation should instead be done via nm_connection_verify().
However, due to another historic oddity, that verification is very
forgiving too and doesn't reject many invalid settings when it should.
That is hard to fix, because making validation more strict can break
existing (and working) configurations. However, verify() already contains
basic validation via nm_setting_bond_validate_option(). So in the previous
behavior nm_setting_bond_add_option() would silently do nothing (only
returning %FALSE) for invalid options, while now it would add the
invalid options to the dictionary -- only to have it later fail validation
during nm_connection_verify(). That is a slight change in behavior, however it
seems preferable.
It seems preferable and acceptable because most users that call
nm_setting_bond_add_option() already understand the meaning and valid
values. Keyfile and ifcfg-rh readers are the few exceptions, which really just
parse a string dictionary, without need to understand them. But nmtui
or nmstate already know the option they want to set. They don't expect
a failure there, nor do they need the validation.
Note that this change in behavior could be dangerous for example for the
keyfile/ifcfg-rh readers, which silently ignored errors before. We
don't want them to start failing if they read invalid options from a
file, so instead let those callers explicitly pre-validate the value
and log an warning.
https://bugzilla.redhat.com/show_bug.cgi?id=1887523
This is potentially a breaking change, formerly speciyfing 'none|off'
in the kernel cmdline option 'ip' was understood by the dracut
network-module as doing 'ipv6.method=auto' which is clearly incosistent
with the 'off' naming, thus 'off|none' now means to actually disable
both ipv6 and ipv4 (unless a static ip is provided).
Unit test added.
https://bugzilla.redhat.com/show_bug.cgi?id=1883958
Reverts: 440a0b4078 ('initrd: set ipv6.method=auto when the autoconfiguration field is 'none'')
Signed-off-by: Antonio Cardace <acardace@redhat.com>
Run:
./contrib/scripts/nm-code-format.sh -i
./contrib/scripts/nm-code-format.sh -i
Yes, it needs to run twice because the first run doesn't yet produce the
final result.
Signed-off-by: Antonio Cardace <acardace@redhat.com>
Having leaks in the tests, breaks running the test under valgrind. There
must be no leaks.
Fixes: c056cb9306 ('initrd: parse 'rd.net.dhcp.vendor-class' kernel cmdline arg')
The "gs_*" macros originate from the (no longer existing) libgsystem library.
We still have them, because so far we didn't go through the effort of
renaming the API.
Aside that oddity, our cleanup API is called "nm_auto*". There is no need
to add new API with the old name.
Since commit 3df662f534 ('settings: rework wait-device-timeout
handling and consider device compatibility'), "connection.wait-device-timeout"
works with profiles in general and doesn't require an interface-name
set.
Remove that restriction and let initrd generator create profiles that
always wait.
The network-legacy dracut module waits for all ethernet devices if the
command line contains rd.neednet=1. It also waits for the device
specified by 'bootdev='.
Do the same.
https://bugzilla.redhat.com/show_bug.cgi?id=1853348
When a 'ip=auto6' option is passed to kernel, the old dracut network
module only sets accept_ra in kernel and wait for the address to
appear. Instead, with a 'ip=dhcp6' option it starts 'dhclient -6',
leaving accept_ra to the initial value (that is already 1). So
'ip=dhcp6' in practice does kernel IPv6 autoconf and DHCPv6 at the
same time, without honoring the 'Managed' flag of the router
advertisement.
It seems that the only reason to have distinct 'auto6' and 'dhcp6'
options was that network module did not support starting DHCPv6 only
when necessary based on the M flag of the RA; so the user had to
specify if DHCPv6 was needed or not.
Given that 1) NM is smarter and can start DHCPv6 only when needed by
RA; 2) DHCPv6 alone only gets a /128 address without a prefix route
and so it's not useful; then it makes sense to generate a connection
with 'ipv6.method=auto' for both 'ip=auto6' and 'ip=dhcp6'.
https://bugzilla.redhat.com/show_bug.cgi?id=1854323https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/571
(cherry picked from commit ca3d0a8f06)
Don't try to open /run/NetworkManager/initrd when called with
--stdout, but instead write the hostname to the standard output.
Fixes: ff70adf873 ('initrd: save hostname to a file in /run')
(cherry picked from commit 5fa97d7796)
There is a bug when parsing a BOOTIF= without any existing
connection. The generated connection doesn't have wired setting and
later we try to access it:
# nm-initrd-generator --stdout -- BOOTIF=01-50-50-00-9f-21-21
(nm-initrd-generator:1546): libnm-CRITICAL **: ((libnm-core/nm-setting-wired.c:205)): assertion '<dropped>' failed
(nm-initrd-generator:1546): GLib-GObject-CRITICAL **: g_object_set: assertion 'G_IS_OBJECT (object)' failed
Fix this.
https://bugzilla.redhat.com/show_bug.cgi?id=1853277
Fixes: 25a2b6e14f ('initrd: rework command line parsing')
(cherry picked from commit 3023c70e4e)
Setting a MTU or a cloned MAC for bonds/bridges/teams fails with:
# nm-initrd-generator -- bond=bond0:eno1,eno2:mode=802.3ad
ip=192.168.1.5::192.168.1.254:255.255.255.0:MyServer:bond0:none::01:02:03:04:05:06
bootdev=bond0 nameserver=192.168.1.1
<warn> cmdline-reader: 'bond' does not support setting cloned-mac-address
Fix this.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/460
(cherry picked from commit 79f70bf5d6)
The 7th field of:
ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}:[:[<mtu>][:<macaddr>]]
specifies which kind of autoconfiguration to do. 'none' and 'off' mean
static addresses.
The old network module of dracut used to leave kernel IPv6
autoconfiguration enabled when IPv4 static addresses were
configured. With NM, this corresponds to enabling IPv6 auto method.
https://bugzilla.redhat.com/show_bug.cgi?id=1848943
(cherry picked from commit a39eb9ac14)
When the initrd generator creates a connection with IPv6 method
'ignore', the kernel will do IPv6 autoconfiguration on the
interface. However, it is preferable to let NetworkManager configure
the interface directly instead of relying on kernel. Therefore, change
the IPv6 method to 'auto'. Note that we still set ipv6.may-fail to
'yes' so that a failure during IPv6 autoconfiguration doesn't bring
down the interface.
(cherry picked from commit f6d654b18f)
nm_keyfile_read() and nm_keyfile_write() will be public API.
As such, it must be flexible and extendible for future needs.
There is already the handler callback that fully solves this
(e.g. a future handler event could request whether a certain
behavior is enabled or not).
As additional possibility for future extension, add a flags
argument. Currently no flags are implemented.
Code like
»···»···if (strcmp (tag, "net.ifnames") == 0)
»···»···»···net_ifnames = strcmp (argument, "0") != 0;
is really hard to understand (at least to me). Compare to
»···»···if (nm_streq (tag, "net.ifnames"))
»···»···»···net_ifnames = !nm_streq (argument, "0");
g_ascii_strtoull() returns a guint64, which is very wrong to directly pass
to the variadic argument list of g_object_set(). We expect a guint there
and need to cast.
While at it, use _nm_utils_ascii_str_to_int64() to parse and validate the input.
$ meson -Dmore_asserts=0 meson-build
$ ninja -C meson-build
[712/859] Compiling C object 'src/initrd/b383957@@nmi-core@sta/nmi-cmdline-reader.c.o'.
../src/initrd/nmi-cmdline-reader.c: In function ‘nmi_cmdline_reader_parse’:
../src/initrd/nmi-cmdline-reader.c:871:4: warning: ‘s_ip’ may be used uninitialized in this function [-Wmaybe-uninitialized]
871 | nm_setting_ip_config_add_dns (s_ip, ns);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/initrd/nmi-cmdline-reader.c:835:21: note: ‘s_ip’ was declared here
835 | NMSettingIPConfig *s_ip;
| ^~~~
Fixes: 25a2b6e14f ('initrd: rework command line parsing')
The 'default_connection' created by the command line parser has
multiple purposes. It's the connection created for 'ip=' arguments
without command line, but is also created when there is a 'bootdev='
or for 'nameserver=' and no other connection exists at the moment the
argument is parsed. This is confusing and leads to a result that
depends on the order of parameters. For example:
$ /usr/libexec/nm-initrd-generator -c connections -- bootdev=eth1 ip=eth0:dhcp
$ ls connections/
default_connection.nmconnection eth0.nmconnection
$ /usr/libexec/nm-initrd-generator -c connections -- ip=eth0:dhcp bootdev=eth1
$ ls connections/
eth0.nmconnection eth1.nmconnection
Make this more explicit by tracking 'bootdev_connection' and
'default_connection' individually.
Also fix handling of 'nameserver', 'rd.peerdns' and 'rd.route'
arguments. First process all connections, and then set those
properties. In particular, now nameservers are applied to all
connections.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/issues/391
Connections are kept in a hash table indexed by name. This causes non
deterministic output in get_conn() when we have to decide a default
connection and no bootdev was specified on the command line.
Also add an array that stores the original order in which interfaces
appear in the command line, and use it when we have to loop through
connections. The return value of nmi_cmdline_reader_parse() is still a
hash table because once we have generated connections, their order
doesn't matter.
Don't add an empty connection to the list if
nmi_ibft_update_connection_from_nic() fails when reading iBFT
information.
If the function fails in parse_ip(), continue with the existing
connection built from other command line options.
Also, fix a memory leak.
g_clear_pointer() would always cast the destroy notify function
pointer to GDestroyNotify. That means, it lost some type safety, like
GPtrArray *ptr_arr = ...
g_clear_pointer (&ptr_arr, g_array_unref);
Since glib 2.58 ([1]), g_clear_pointer() is also more type safe. But
this is not used by NetworkManager, because we don't set
GLIB_VERSION_MIN_REQUIRED to 2.58.
[1] f9a9902aac
We have nm_clear_pointer() to avoid this issue for a long time (pre
1.12.0). Possibly we should redefine in our source tree g_clear_pointer()
as nm_clear_pointer(). However, I don't like to patch glib functions
with our own variant. Arguably, we do patch g_clear_error() in
such a manner. But there the point is to make the function inlinable.
Also, nm_clear_pointer() returns a boolean that indicates whether
anything was cleared. That is sometimes useful. I think we should
just consistently use nm_clear_pointer() instead, which does always
the preferable thing.
Replace:
sed 's/\<g_clear_pointer *(\([^;]*\), *\([a-z_A-Z0-9]\+\) *)/nm_clear_pointer (\1, \2)/g' $(git grep -l g_clear_pointer) -i
I think it's preferable to use nm_clear_g_free() instead of
g_clear_pointer(, g_free). The reasons are not very strong,
but I think it is overall preferable to have a shorthand for this
frequently used functionality.
sed 's/\<g_clear_pointer *(\([^;]*\), *\(g_free\) *)/nm_clear_g_free (\1)/g' $(git grep -l g_clear_pointer) -i
In all the cases, we don't want to perform locale dependent comparison.
$ sed -i 's/\<strcasecmp\>/g_ascii_\0/g' $(git grep -w -l strcasecmp -- ':(exclude)shared/systemd/' )
We should use the same "is-valid" function everywhere.
Since nm_utils_ipaddr_valid() is part of libnm, it does not qualify.
Use nm_utils_ipaddr_is_valid() instead.
Do process the connections from the iBFT block if the rd.iscsi.ibft or
rd.iscsi.ibft=1 argument is present.
This is supposed to fix what was originally reported by Kairui Song
<kasong@redhat.com> here: https://github.com/dracutdevs/dracut/pull/697
If an argument in form ip=eth0:ibft is specified, we'd first create a
wired connection with con.interface-name and then proceed completing it
from the iBFT block. At that point we also add the MAC address, so the
interface-name is no longer necessary..
Worse even, for VLAN connections, it results in an attempt to create
a VLAN with the same name as the parent wired device. Ooops.
Let's just drop it. MAC address is guarranteed to be there and does the
right thing for both plain wired devices as well as VLANs.
Keyfile support was initially added under GPL-2.0+ license as part of
core. It was moved to "libnm-core" in commit 59eb5312a5 ('keyfile: merge
branch 'th/libnm-keyfile-bgo744699'').
"libnm-core" is statically linked with by core and "libnm". In
the former case under terms of GPL-2.0+ (good) and in the latter case
under terms of LGPL-2.1+ (bad).
In fact, to this day, "libnm" doesn't actually use the code. The linker
will probably remove all the GPL-2.0+ symbols when compiled with
gc-sections or LTO. Still, linking them together in the first place
makes "libnm" only available under GPL code (despite the code
not actually being used).
Instead, move the GPL code to a separate static library
"shared/nm-keyfile/libnm-keyfile.la" and only link it to the part
that actually uses the code (and which is GPL licensed too).
This fixes the license violation.
Eventually, it would be very useful to be able to expose keyfile
handling via "libnm". However that is not straight forward due to the
licensing conflict.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/381
inet_aton() is very accepting when parsing the address. For example,
it accepts addresses with fewer octets (interpreting the last octet
as a number in network byte order for multiple bytes). It also ignores
any trailing garbage after the first delimiting whitespace (at least,
the glibc implementation). It also accepts octets in hex and octal
notation.
For the initrd reader we want to be more forgiving than inet_pton()
and also accept addresses like 255.000.000.000 (octal notation). For
that we would want to use inet_aton(). But we should not accept all the
craziness that inet_aton() otherwise accepts.
Use nm_utils_parse_inaddr_bin_full() instead. This function implements
our way how we want to interpret IP addresses in string representation.
Under the hood, of course it also uses inet_pton() and even inet_aton(),
but it is stricter than inet_aton() and only accepts certain formats.
(cherry picked from commit d68373c305)