Compare commits

...

63 commits
main ... 1.54.3

Author SHA1 Message Date
Íñigo Huguet
8caccc6bed release: bump version to 1.54.3 2025-12-12 16:24:05 +01:00
Íñigo Huguet
c8384fd528 std-aux: use _nm_strerror_r
The function strerror_r returns an int per POSIX spec, but GNU version
returns char *. Using it fails the compilation in Alpine, so use
_nm_strerror_r instead that handles both cases.

Fixes: 41e28b900f ('daemon-helper: add read-file-as-user')
(cherry picked from commit 599cc1ed1d)
(cherry picked from commit ea759ccf3a)
2025-12-12 15:41:17 +01:00
Íñigo Huguet
8efa30db90 merge: branch 'nm-1-54-issue1809'
[nm-1-54] CVE-2025-9615: avoid that non-admin user using other users' certificates

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2326
2025-12-12 13:03:26 +00:00
Íñigo Huguet
4393b37686 nm-version: set API_VERSION with MICRO+1 (temporary)
In the past, stable branches used odd micro numbers as development micro
version. Because of that, NM_API_VERSION was defined with MICRO+1 so we
don't get warnings during development.

As we stopped using odd micro=devel it is wrong to set MICRO+1 on odd
releases. Final users of 1.52.3 has NM_API_VERSION 1.52.4.

However, during development we need to have MICRO+1. For example, if we
are working on top of 1.52.3 towards the next 1.52.4, we define new
symbols with NM_AVAILABLE_IN_1_52_4. Because of that, we get compilation
failures until we finally bump to 1.52.4, just before the release. The
CI remains red until then, potentially missing many bugs.

For now, just set MICRO+1 all the time. It is wrong, but it was wrong
half of the time anyway, and at least we'll have a green CI until we
implement a definitive solution.

(cherry picked from commit 13bfa44ceb)
2025-12-12 13:34:06 +01:00
Beniamino Galvani
4587832735 libnm: add function to copy a certificate or key as user
Add a new public function nm_utils_copy_cert_as_user() to libnm. It
reads a certificate or key file on behalf of the given user and writes
it to a directory in /run/NetworkManager. It is useful for VPN plugins
that run as root and need to verify that the user owning the
connection (the one listed in the connection.permissions property) can
access the file.

(cherry picked from commit 1a52bbe7c9)
(cherry picked from commit 3d85bace3d)
2025-12-12 13:34:04 +01:00
Beniamino Galvani
15346f1a4f vpn: add nm_vpn_plugin_info_supports_safe_private_file_access()
The new API indicates that the VPN plugin supports reading files
(certificates, keys) of private connections in a safe way
(i.e. checking user permissions), or that it doesn't need to read any
file from disk.

(cherry picked from commit 10db4baeb6)
(cherry picked from commit 8437e14758)
2025-12-12 13:33:38 +01:00
Íñigo Huguet
ce4ec4ac80 libnm: introduce NM_VERSION_1_54_3 2025-12-10 13:17:29 +01:00
Beniamino Galvani
9bc4d62680 core,libnm-core: introduce property flag for certificate and keys
If we add a new property in the future and it references a certificate
or key stored on disk, we need to also implement the logic to verify
the access to the file for private connections.

Add a new property flag NM_SETTING_PARAM_CERT_KEY_FILE to existing
certificate and key properties, so that it's easier to see that they
need special treatment. Also add some assertions to verify that the
properties with the flag are handled properly.

While at it, move the enumeration of private-files to the settings.

(cherry picked from commit acbfae5e05)
(cherry picked from commit e3c27f2a22)
2025-12-10 10:50:04 +01:00
Beniamino Galvani
f08ee617b9 core: pass certificates as blobs to supplicant for private connections
In case of private connections, the device has already read the
certificates and keys content from disk, validating that the owner of
the connection has access to them. Pass those files as blobs to the
supplicant so that it doesn't have to read them again from the
filesystem, creating the opportunity for TOCTOU bugs.

(cherry picked from commit 36ea70c099)
(cherry picked from commit aac5b80fca)
2025-12-10 10:50:02 +01:00
Beniamino Galvani
b8f8731636 device: read private files in stage2
During stage2 (prepare) of an activation, check if the connection is
private and if it contains any certificate/key path. If so, start
reading the files and delay stage2. Once done, store the files'
content into priv->private_files.table and continue the activation.

(cherry picked from commit 98e6dbdf21)
(cherry picked from commit a417df3484)
2025-12-10 10:48:06 +01:00
Beniamino Galvani
399d7be771 core: add functions to read private files of connections
Add function nm_utils_read_private_files(). It can be used to read a
list of paths as the given user. It spawns the daemon-helper to read
each path and returns asynchronously a hash table containing the files
content.

Also add nm_utils_get_connection_private_files_paths() to return a
list of file paths referenced in a connection. The function currently
returns only 802.1x file paths for certificates and keys.

(cherry picked from commit de4eb64253)
(cherry picked from commit 9432822f34)
2025-12-10 10:48:05 +01:00
Beniamino Galvani
b7926872e1 supplicant: rename variables
Rename uid to to blob_id, and con_uid to con_uuid.

(cherry picked from commit 586f7700b8)
(cherry picked from commit a17f51fe15)
2025-12-10 10:48:05 +01:00
Beniamino Galvani
7acf70dfb9 core: support returning binary output from the daemon helper
The full output of the daemon helper is added to a NMStrBuf, without
interpreting it as a string (that is, without stopping at the first
NUL character).

However, when we retrieve the content from the NMStrBuf we assume it's
a string. This is fine for certain commands that expect a string
output, but it's not for other commands as the read-file-as-user one.

Add a new argument to nm_utils_spawn_helper() to specify whether the
output is binary or not. Also have different finish functions
depending on the return type.

(cherry picked from commit 1d90d50fc6)
(cherry picked from commit 59df5fc93f)
2025-12-10 10:48:04 +01:00
Beniamino Galvani
4f3597448d supplicant: remove blobs before adding new ones
When connecting, we add the blobs to the Interface object of the
supplicant. Those blobs are not removed on disconnect and so when we
try to add blobs with the same id, the supplicant returns an error.

Make sure we start from a clean slate on each connection attempt, by
deleting all existing blobs. Probably we should also delete the added
blobs on disconnect, but that's left for a future improvement.

(cherry picked from commit 0093bbd950)
(cherry picked from commit ce3ebf6d3e)
2025-12-10 10:48:04 +01:00
Beniamino Galvani
310887be71 daemon-helper: add read-file-as-user
Add a new command to read the content of a file after switching to the
given user. This command can be used to enforce Unix filesystem
permissions when accessing a file on behalf of a user.

(cherry picked from commit 285457a5f8)
(cherry picked from commit 022b992846)
2025-12-10 10:48:04 +01:00
Beniamino Galvani
d1776c5394 helpers: move helper programs to the same directory
Create a new 'nm-helpers' directory for all the helper programs, to
avoid having too many subdirs in the src directory.

(cherry picked from commit 3d76d12eee)
(cherry picked from commit afa6fc951b)
2025-12-10 10:48:04 +01:00
Beniamino Galvani
abdf3385d6 libnm-core, core: add permission helpers
Add utility functions to get the number of users and the first user
from the connection.permissions property of a connection.

(cherry picked from commit 59543620dc)
(cherry picked from commit 2fc662cc71)
2025-12-10 10:48:02 +01:00
Íñigo Huguet
dc3beb479f release: bump version to 1.54.2 2025-11-17 16:41:26 +01:00
Íñigo Huguet
2ef71cf17d merge: branch 'ih/forwarding'
core: ip forwarding: improve performance setting IPv4 forwarding for method=shared and forwarding=auto

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2201

(cherry picked from commit 384dd7d5f2)
2025-11-17 16:08:35 +01:00
Íñigo Huguet
6d3cfda43e core: mark as managed-type=removed when unrealize
This is needed to ensure that the right CleanupType is chosen when
calling to nm_device_state_changed() a bit later. With this change
CLEANUP_TYPE_REMOVED will be used instead of CLEANUP_TYPE_DECONFIGURE,
which is wrong because the device has already disappeared.

(cherry picked from commit e06aaba1ca)
2025-11-17 16:08:33 +01:00
Íñigo Huguet
2a264ee841 ip: shared: stop using the global forwarding
As we introduced the ipv4.forwarding property in a8a2e6d727 ('ip-config:
Support configuring per-device IPv4 sysctl forwarding option'), we must
not enable or disable the global forwarding setting in the kernel, as it
affects to all the devices, maybe forcing them to behave in a way
different to what the user requested in ipv4.forwarding.

Instead, we need to selectively enable or disable the per-device forwarding
settings. Specifically, only devices activated with ipv4.forwarding=auto
must have their forwarding enabled or disabled depending on shared
connections. Devices with yes/no must not be affected by shared connections.

Also, devices with ipv4.forwarding=auto must get the proper forwarding value
on activation, but also change it when shared connections appear or
disappear dynamically. Use the new sharing-ipv4-change signal from
nm_manager to achieve it.

Fixes: a8a2e6d727 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option')
(cherry picked from commit 32cbf4c629)
2025-11-17 16:08:32 +01:00
Íñigo Huguet
d8e3561eaf manager: add sharing-ipv4-changed signal
This signal notifies about the "sharing state", that's it, when there
is at least one shared connection active or not. Each device informs
to nm_manager when a shared connection is activated or deactivated
and nm_manager emits this signal when the first shared connection is
activated or the last one is deactivated.

For now we're only interested in IPv4 forwarding as it's the only one
that we need to track from nm_device (in following commits).

Fixes: a8a2e6d727 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option')
(cherry picked from commit 8faa33b9d4)
2025-11-17 16:08:32 +01:00
Íñigo Huguet
edf0dd2901 ip: restore IP forwarding on device deconfiguration
With the ipv4.forwarding property we may modify the forwarding sysctl of
the device on activation. In next commits, we will also modify it if the
connection is shared, instead of modifying the global forwarding.

Restore the forwarding value to the default one when the device is
deconfigured for any reason.

Fixes: a8a2e6d727 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option')
(cherry picked from commit d58d0a793e)
2025-11-17 16:08:31 +01:00
Íñigo Huguet
62d6ff49e4 Revert "policy: refresh IPv4 forwarding after connection activation and disconnection"
This reverts commit 2ad5fbf025.

It is actually a partial revert. The changes to documentation don't need
to be reverted.

Fixes: 2ad5fbf025 ('policy: refresh IPv4 forwarding after connection activation and disconnection')
(cherry picked from commit f2a2e49d07)
2025-11-17 16:08:31 +01:00
Íñigo Huguet
11482237e4 core: rename unmanaged flag SLEEPING->MANAGER_DISABLED
The flag is used for both sleeping and networking disabled conditions.
This is because internally they share logic, but it's not obvious for
users and it has caused confusion in the past when investigating why
devices didn't become managed. Make it explicit that it can be because
of either reason.

It would be better to create two separate flags, actually, and it
doesn't seem complex, but better not to risk introducing bugs for that
little benefit.

Logs before:
  device (enp4s0): state change: disconnected -> unmanaged (reason 'unmanaged-sleeping' ...

Logs before:
  device (enp4s0): state change: disconnected -> unmanaged (reason 'unmanaged-nm-disabled' ...

(cherry picked from commit 48fc40e1ca)
2025-11-17 14:41:55 +01:00
Íñigo Huguet
21b89a1267 core, libnm: add the "networking off" reason
When we disable networking with `nmcli networking off` the reason that
is logged is "sleeping". Explain instead that networking is disabled.

Before:
  device (lo): state change: activated -> deactivating (reason 'sleeping' ...

After:
  device (lo): state change: activated -> deactivating (reason 'networking-off' ...
(cherry picked from commit f6d6a7e2eb)
2025-11-17 14:41:54 +01:00
Íñigo Huguet
51fc8af385 core: rename NM_STATE_ASLEEP to NM_STATE_DISABLED
When we do `nmcli networking off` it's shown as state "sleeping". This
is confusing, and the only reason is that we share internally code to
handle both situations in a similar way.

Rename the state to the more generic name "disabled", situation that can
happen either because of sleeping or networking off.

Clients cannot differentiate the exact reason only with the NMState value,
but better that they show "network off" as this is the most common reason
that they will be able to display. If the system is suspending, there will
be only a short period of time that they can show the state, and showing
"network off" is not wrong because that's what NM has done as a response
to suspend.

In the logs, let's make explicit the exact reason why state is changing
to DISABLED: sleeping or networking off.

Logs before:
  manager: disable requested (sleeping: no  enabled: yes)
  manager: NetworkManager state is now ASLEEP

Logs after:
  manager: disable requested (sleeping: no  enabled: yes)
  manager: NetworkManager state is now DISABLED (NEWORKING OFF)

State before:
  $ nmcli general
  STATE  ...
  asleep ...

State after:
  $ nmcli general
  STATE       ...
  network off ...

(cherry picked from commit 3355ba9380)
2025-11-17 14:41:53 +01:00
Íñigo Huguet
1423c4c46d NEWS: update 2025-11-17 14:41:53 +01:00
Jan Vaclav
9449189b50 platform: configure HSR interlink from property
Uses the `hsr.interlink` property defined in the previous
commit to configure the property in the kernel.

(cherry picked from commit 17efec8b06)
2025-11-17 14:41:52 +01:00
Jan Vaclav
dba8ba8e6f libnm: introduce hsr.interlink property
This property allows the user to optionally configure
an interlink name on a HSR interface, so that it could
serve as RedBox (Redundant Box) by connecting DAN (dual
attachment node) to SAN (single attachment node).

(cherry picked from commit 69d0fb161e)
2025-11-17 14:41:49 +01:00
Jan Vaclav
cd3d492618 platform: configure HSR protocol version from property
Uses the `hsr.protocol-version` property defined in the previous
commit to configure the property in the kernel.

(cherry picked from commit 0b99629278)
2025-11-17 14:41:27 +01:00
Jan Vaclav
d3d545deca libnm: introduce hsr.protocol-version property
This property allows the user to set the protocol
version when using HSR. Currently, the property
supports two values - `2010` (referred to as HSRv0
in the kernel), and `2012` (HSRv1).

(cherry picked from commit 9a2395c779)
2025-11-17 14:41:22 +01:00
Íñigo Huguet
afc1a88f64 libnm: add NM_VERSION_1_50_4 2025-11-17 13:54:00 +01:00
Íñigo Huguet
b25276135c NEWS: update 2025-10-15 09:47:55 +02:00
Jan Vaclav
6f454c98a9 device: add support for reapplying the sriov.vfs property
Adds support for reapplying the `sriov.vfs` property. Note this
does not include `num_vfs`, as the configuration needs to be reset
and reconfigured from scratch in that case.

Previously, if an existing VF is modified (e.g. if we change the `trust`
flag), we reset all VF configurations, and started from scratch. But in
some cases, this is unnecessarily disruptive.

Resolves: https://issues.redhat.com/browse/RHEL-95844
(cherry picked from commit 4ba3ffee67)
2025-10-15 09:43:50 +02:00
Jan Vaclav
b2d8f60c49 device: extract sriov platform vf generation to separate function
(cherry picked from commit 588a69cd1b)
2025-10-15 09:43:49 +02:00
Íñigo Huguet
c0048e0d26 merge: branch 'ih/ovs-external-ports'
ovs: don't remove unrelated external ports

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2296

(cherry picked from commit e6a31264c1)
2025-10-15 08:59:04 +02:00
Íñigo Huguet
6f9ba5fcc5 man: ovs: document known limitation when removing ifaces and ports
Document a known limitation that we delete bridges and ports from ovsdb
when we remove their last NM-owned attached port or interface, even if
other externally added ports or interfaces exist.

(cherry picked from commit 86ea2c5963)
2025-10-15 08:59:04 +02:00
Íñigo Huguet
8326cc32d6 ovs: don't remove unrelated external ports
The commit linked below introduced a bug that caused that OVS ports
added externally to NM are always deleted when we delete any OVS
interface. It affects to all externally added ports, including those
that are not related to the deleted interface and even those in
different OVS bridges.

Fix it by only modifying ports and bridges that are ascendants of the
deleted interface, leaving everything else untouched.

Note that bridges and ports still need to have at least one NM-managed
interface, otherwise they will also be purged. For example, an NM-owned
OVS bridge with 2 ports+iface, one NM-owned and one external: if we
delete the NM-owned iface, both ports and the bridge will be deleted.
For now, this is a known limitation that is not being fixed here.

Fixes: 476c89b6f2 ('ovs: only keep bridges and ports with NM interfaces attached')
(cherry picked from commit 93491d76ec)
2025-10-15 08:59:04 +02:00
Íñigo Huguet
2450d860c6 release: bump version to 1.54.1 2025-09-12 14:56:56 +02:00
Íñigo Huguet
ec0db2b22e NEWS: update 2025-09-12 14:56:01 +02:00
Beniamino Galvani
824ebf8c2e libnm-core: honor secrets flags when serializing WireGuard peers to D-Bus
If "flags" indicate that only secrets should be serialized and a peer
doesn't contain any secrets, skip it. Otherwise the function would
return a non-empty result when the connection contains no secret,
which causes issues later in the agent manager.

Fixes: e148ec07d5 ('libnm: add NMWireGuardPeer and libnm support for peers')

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2244
(cherry picked from commit 86c7f1ed14)
2025-09-12 13:21:46 +02:00
Íñigo Huguet
0c4647d507 wifi: don't recheck auto-activate on disposal
During disposal we're calling to remove_all_aps that in turns schedules
an auto-activate recheck. As the device is removed, this triggers an
assertion when trying to do the recheck.

Fix that by not scheduling the recheck.

Example of backtrace that this commits fix:
  0  __libc_do_syscall () at ../sysdeps/unix/sysv/linux/arm/libc-do-syscall.S:47
  1  0xf746e270 in __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=<optimized out>) at pthread_kill.c:43
  2  0xf743fbc6 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
  3  0xf7431614 in __GI_abort () at abort.c:79
  4  0xf775afea in g_assertion_message (domain=domain@entry=0x209a9f "nm", file=file@entry=0x1f7d59 "../NetworkManager-1.43.7/src/core/nm-policy.c", line=line@entry=1665,
     func=func@entry=0x1f94d9 <__func__.6> "nm_policy_device_recheck_auto_activate_schedule",
     message=message@entry=0x1d3e950 "assertion failed: (g_signal_handler_find(device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NM_POLICY_GET_PRIVATE(self)) != 0)")
     at ../glib-2.72.3/glib/gtestutils.c:3253
  5  0xf775b05e in g_assertion_message_expr (domain=0x209a9f "nm", file=0x1f7d59 "../NetworkManager-1.43.7/src/core/nm-policy.c", line=1665,
     func=0x1f94d9 <__func__.6> "nm_policy_device_recheck_auto_activate_schedule",
     expr=0x1f8afc "g_signal_handler_find(device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NM_POLICY_GET_PRIVATE(self)) != 0") at ../glib-2.72.3/glib/gtestutils.c:3279
  6  0x0005f27a in nm_policy_device_recheck_auto_activate_schedule (self=0x1d3e950, device=0x209a9f) at ../NetworkManager-1.43.7/src/core/nm-policy.c:1679
  7  0x000548ae in nm_manager_device_recheck_auto_activate_schedule (self=<optimized out>, device=<optimized out>) at ../NetworkManager-1.43.7/src/core/nm-manager.c:3113
  8  0x00070622 in nm_device_recheck_auto_activate_schedule (self=<optimized out>) at ../NetworkManager-1.43.7/src/core/devices/nm-device.c:9249
  9  0xf693aa8c in ap_add_remove (self=self@entry=0x1ceb0b0, is_adding=0, ap=<optimized out>, recheck_available_connections=0)
     at ../NetworkManager-1.43.7/src/core/devices/wifi/nm-device-wifi.c:846
  10 0xf693bcda in remove_all_aps (self=self@entry=0x1ceb0b0) at ../NetworkManager-1.43.7/src/core/devices/wifi/nm-device-wifi.c:863
  11 0xf693f83c in dispose (object=0x1ceb0b0) at ../NetworkManager-1.43.7/src/core/devices/wifi/nm-device-wifi.c:3809
  12 0xf7806e72 in g_object_unref (_object=<optimized out>) at ../glib-2.72.3/gobject/gobject.c:3636
  13 g_object_unref (_object=0x1ceb0b0) at ../glib-2.72.3/gobject/gobject.c:3553
  14 0x000f7fa4 in _nm_dbus_object_clear_and_unexport (location=location@entry=0xffa50644) at ../NetworkManager-1.43.7/src/core/nm-dbus-object.c:203
  15 0x000576e4 in remove_device (self=self@entry=0x1c9c900, device=<optimized out>, quitting=quitting@entry=1) at ../NetworkManager-1.43.7/src/core/nm-manager.c:2289
  16 0x0005a864 in nm_manager_stop (self=self@entry=0x1c9c900) at ../NetworkManager-1.43.7/src/core/nm-manager.c:7784
  17 0x00023438 in main (argc=<optimized out>, argv=<optimized out>) at ../NetworkManager-1.43.7/src/core/main.c:530

Fixes: 96f40dcdcd ('wifi/ap: explicitly unexport AP and refactor add/remove AP')
Fixes: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1791
(cherry picked from commit 3904135150)
2025-09-12 13:21:45 +02:00
Beniamino Galvani
dff8f43adf initrd: support setting the DHCP client-id
In some cases it is necessary to set a custom DHCP client-id during
early boot. For example, the firmware of some InfiniBand NIC uses a
48-bit MAC derived from the InfiniBand 20-byte MAC when doing
PXE. NetworkManager doesn't have any knowledge of that 48-bit MAC and
uses the full MAC as client-id, therefore getting a different lease.

Introduce a new option 'rd.net.dhcp.client-id' to specify a custom
client-id.

Resolves: https://issues.redhat.com/browse/RHEL-108454

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2277
(cherry picked from commit 40aa27690c)
2025-09-12 13:20:45 +02:00
Íñigo Huguet
44e406c1de merge: branch 'ih/dbus-global-dns'
core: dns: fix the behavior of [global-dns]

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2261

(cherry picked from commit f472111e58)
2025-09-12 13:20:15 +02:00
Íñigo Huguet
2de3de0c28 core: keep empty groups from keyfile configs
When reading NetworkManager.conf and NetworkManager-intern.conf we might
need to know if a group is defined or not, even if it's empty. This is
the case, for example, for [global-dns]. If [global-dns] is defined in
NM.conf overwrites the config from NM-intern, and if it's defined in any
of them they overwrite the configs from connections.

Before this patch, defining it as an empty group was ignored:
```
[global-dns]
```

Instead, it was necessary to add at least one key-value to the group.
Otherwise the group was silently ignored.
```
[global-dns]
searches=
```

Keep empty groups so we can take better decissions about overwritting
configs from other sources.

(cherry picked from commit 4a46f454da)
2025-09-12 13:20:14 +02:00
Íñigo Huguet
58e776c3a8 core: dns: show in D-Bus if [global-dns] is defined but empty
Clients like nmstate needs to know if the [global-dns] section is
defined or not, so they know if DNS configs from connections are
relevant or not. Expose it in D-Bus by always exposing "searches"
and "options" if it's defined, maybe as empty lists.

(cherry picked from commit 7fb4724efa)
2025-09-12 13:20:14 +02:00
Íñigo Huguet
f92a2bfd1f dns: make [global-dns] to overwrite configs from connections
According to the documentation, settings from [global-dns] (searches and
options) are always merged with those from connections. However this was
not happening if no [global-dns-domain-*] exists, in which case
connections were ignored. This happened because in the past both global
sections must de defined or undefined. When this was changed to allow
defining only [global-dns], allowing it in the function that generates
the resolv.conf file was forgotten. Fix that now.

Anyway, merging these configs doesn't make much sense. The searches and
options defined in connections probably make sense only for the nameservers
defined in that same connection.

Because of this, make the following change: if global nameservers are
defined, use searches and options from [global-dns] only, because those
defined in connections may not make sense for the global nameservers. If
[global-dns] is missing, assume an empty [global-dns] section.

Also, if no global nameservers are defined, but [global-dns] is, make
that it overwrites the searches and options defined in connections. This
is not ideal, but none of the alternatives is better and at least this
is easy to remember.

So, the resulting rules from above are:
- If [global-dns] is defined, it always overwrite searches and options
  from connections.
- If [global-dns-domain-*] is defined, it always overwrite nameservers
  from connections. It overwrites searches and options too.

Fixes: 1f0d1d78d2 ('dns-manager: always apply options from [global-dns]')
Fixes: f57a848da5 ('man: update documentation about global DNS configuration')
(cherry picked from commit 1cba0a3cca)
2025-09-12 13:20:13 +02:00
Íñigo Huguet
48eae2e76d core: dbus: accept global DNS configuration without a default domain
Since 1.44 we accept a global-dns section without any global-dns-domain
section, so users can define searches and options without defining any
global DNS servers.

When set from the D-Bus API it was still rejected. Fix it.

Fixes: 1f0d1d78d2 ('dns-manager: always apply options from [global-dns]')
(cherry picked from commit 294131a2a4)
2025-09-12 13:20:13 +02:00
Pradyumn Rahar
ec93c9d282 bond: remove lacp_active option from reapply subset
NM_SETTING_BOND_OPTION_LACP_ACTIVE is flagged as BOND_OPTFLAG_IFDOWN in
the kernel and hence should not be in OPTIONS_REAPPLY_SUBSET.

Authored-by: Mohith Kumar Thummaluru <mohith.k.kumar.thummaluru@oracle.com>
Signed-off-by: Mohith Kumar Thummaluru <mohith.k.kumar.thummaluru@oracle.com>
Signed-off-by: Pradyumn Rahar <pradyumn.rahar@oracle.com>
(cherry picked from commit 9c48bae3b2)
2025-09-12 13:19:31 +02:00
Beniamino Galvani
de46cf1ee9 manager: unrealize device again after failure
If the device was realized in _internal_activate_device() and the
activation failed to start, unrealize the device again so that it
doesn't stay around.

Fixes-test: @ovs_delete_connecting_interface

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2263
(cherry picked from commit 7242c478a1)
2025-09-12 13:17:01 +02:00
Beniamino Galvani
7e1c72fa63 device: explicitly handle unrealized devices in is_available()
Unrealized software devices are always available for activation,
hardware devices never.

In nm_manager_get_best_device_for_activation() we call
nm_device_is_available() on candidate devices. Without this fix, any
unrealized software device would be not considered ready for
activation, which is wrong.

A software device can override the default implementation of
is_available(). For example NMDeviceOvsInterface does that and only
checks the OVSDB is ready.

Fixes: ba86c208e0 ('Revert "core: prevent the activation of unavailable OVS interfaces only"')

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2253
(cherry picked from commit 8b26cb35ee)
2025-09-12 13:15:18 +02:00
Íñigo Huguet
96a8982e5d merge: branch 'ih/update-distros-1.54'
[nm-1-54] ci: update distros

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2272
2025-09-08 13:50:37 +00:00
Íñigo Huguet
7121b42179 [nm-1-54] ci: update distros 2025-09-08 13:01:10 +02:00
Íñigo Huguet
a25a6f5a1e merge: branch 'bg/test-client-quotation'
test-client: fix quotation in expected message

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2269

(cherry picked from commit 57ea2cf612)
2025-09-08 13:00:42 +02:00
Beniamino Galvani
6cd3d19ae9 libnm-client: fix memory leak
The test calls g_main_context_ref() on client_context, which must then
be unreferenced.

Fixes: 88724ff169 ('libnm: add nm_client_wait_shutdown() function for cleaning up NMClient')
(cherry picked from commit d559f61423)
2025-09-08 13:00:42 +02:00
Beniamino Galvani
cb61701649 test-client: fix quotation in expected message
With the newer glib and libc in F42 and Ubuntu devel, the
"{left,right} double quotation mark" characters are printed in the
output message. The double quotation marks are multi-byte characters
and they can't be matched using a character class []. Update the
regexp accordingly.

(cherry picked from commit 5ad712974e)
2025-09-08 13:00:42 +02:00
Beniamino Galvani
1f23bb18ad device: ensure that sw devices are unrealized after connection deletion
When a software device becomes deactivated, we check whether it can
be unrealized (= deleted in kernel), by calling function
delete_on_deactivate_check_and_schedule().

The function returns without doing anything if there is a new
activation enqueued on the device (priv->queued_act_request), because
in that case the device will be reused for the next activation.

This commit fixes a problem seen in NMCI test
@ovs_delete_connecting_interface: sometimes the device is not
unrealized after deleting the connection. That happens because if the
queued activation fails, we never try again to unrealize the device.

Fix that by calling delete_on_deactivate_check_and_schedule() when
there is a failure starting the queued activation.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2258
(cherry picked from commit 0b03614b68)
2025-08-27 13:12:47 +02:00
Beniamino Galvani
a14f07484e merge: branch 'nbft-parser-2'
nm-initrd-generator: Add NBFT tests

Closes #1756

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2256

(cherry picked from commit 21bbe24fee)
2025-08-26 10:04:44 +02:00
Tomas Bzatek
ccd5a0226b nm-initrd-generator: add NBFT parser tests
This adds simple unit tests for most common NBFT deployments.
Sample data were mostly taken from the upstream libnvme repository.

(cherry picked from commit dce149352d)
2025-08-26 10:04:44 +02:00
Tomas Bzatek
32f801dec8 nm-initrd-generator: fix a conn_name leak
(cherry picked from commit 592a4f077b)
2025-08-26 10:04:44 +02:00
Tomas Bzatek
2df9da3d2b nm-initrd-generator: rework NBFT HFI DHCP detection
There are several flags specified in the NVMe Boot Specification
that may indicate DHCP was used to acquire information during the
pre-OS phase. This commit considers these additional sources,
based on actual NBFT table contents from different systems.

Although we've seen slight variations in firmware implementations
regarding the HFI IP Origin values when DHCP was configured, the
new set of rules still align with expectations.

(cherry picked from commit 6304d51440)
2025-08-26 10:04:44 +02:00
Beniamino Galvani
862277bda9 rpm: change system_ca_path
/etc/pki/tls/cert.pem was a symlink to
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem since Fedora 19 and
it is being removed in Fedora 43.

See https://fedoraproject.org/wiki/Changes/droppingOfCertPemFile

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2257
https://bugzilla.redhat.com/show_bug.cgi?id=2380436
(cherry picked from commit 94d7dd9b11)
2025-08-25 15:36:25 +02:00
102 changed files with 3342 additions and 731 deletions

View file

@ -60,11 +60,11 @@ variables:
#
# This is done by running `ci-fairy generate-template` and possibly bumping
# ".default_tag".
ALPINE_TAG: 'tag-da1ae96102c6'
CENTOS_TAG: 'tag-c8df7d2b249f'
DEBIAN_TAG: 'tag-e68f538711ec'
FEDORA_TAG: 'tag-c8df7d2b249f'
UBUNTU_TAG: 'tag-e68f538711ec'
ALPINE_TAG: 'tag-dcc430216167'
CENTOS_TAG: 'tag-feb1adbc208e'
DEBIAN_TAG: 'tag-afb784497c2f'
FEDORA_TAG: 'tag-feb1adbc208e'
UBUNTU_TAG: 'tag-afb784497c2f'
ALPINE_EXEC: 'bash .gitlab-ci/alpine-install.sh'
CENTOS_EXEC: 'bash .gitlab-ci/fedora-install.sh'
@ -102,7 +102,19 @@ variables:
# Build a container for each distribution + version. The ci-templates
# will re-use the containers if the tag doesn't change.
tier1:fedora:42@prep:
tier1:fedora:43@prep:
extends:
- .fdo.container-build@fedora
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
FDO_DISTRIBUTION_EXEC: $FEDORA_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule' || $SCHEDULED_PIPELINE_NAME == "weekly"
tier3:fedora:42@prep:
extends:
- .fdo.container-build@fedora
stage: prep
@ -111,88 +123,6 @@ tier1:fedora:42@prep:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
FDO_DISTRIBUTION_EXEC: $FEDORA_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule' || $SCHEDULED_PIPELINE_NAME == "weekly"
tier2:fedora:rawhide@prep:
extends:
- .fdo.container-build@fedora
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'rawhide'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
FDO_DISTRIBUTION_EXEC: $FEDORA_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier2:centos:stream9@prep:
extends:
- .fdo.container-build@centos
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'stream9'
FDO_DISTRIBUTION_TAG: $CENTOS_TAG
FDO_DISTRIBUTION_EXEC: $CENTOS_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier2:ubuntu:devel@prep:
extends:
- .fdo.container-build@ubuntu
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'devel'
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
FDO_DISTRIBUTION_EXEC: $UBUNTU_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier2:debian:testing@prep:
extends:
- .fdo.container-build@debian
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'testing'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
FDO_DISTRIBUTION_EXEC: $DEBIAN_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier2:debian:sid@prep:
extends:
- .fdo.container-build@debian
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'sid'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
FDO_DISTRIBUTION_EXEC: $DEBIAN_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier2:alpine:edge@prep:
extends:
- .fdo.container-build@alpine
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'edge'
FDO_DISTRIBUTION_TAG: $ALPINE_TAG
FDO_DISTRIBUTION_EXEC: $ALPINE_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
@ -226,20 +156,6 @@ tier3:ubuntu:25.04@prep:
when: manual
allow_failure: true
tier3:ubuntu:24.10@prep:
extends:
- .fdo.container-build@ubuntu
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '24.10'
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
FDO_DISTRIBUTION_EXEC: $UBUNTU_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier3:ubuntu:24.04@prep:
extends:
- .fdo.container-build@ubuntu
@ -268,6 +184,20 @@ tier3:ubuntu:22.04@prep:
when: manual
allow_failure: true
tier3:debian:13@prep:
extends:
- .fdo.container-build@debian
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '13'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
FDO_DISTRIBUTION_EXEC: $DEBIAN_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier3:debian:12@prep:
extends:
- .fdo.container-build@debian
@ -282,6 +212,20 @@ tier3:debian:12@prep:
when: manual
allow_failure: true
tier3:alpine:3.22@prep:
extends:
- .fdo.container-build@alpine
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '3.22'
FDO_DISTRIBUTION_TAG: $ALPINE_TAG
FDO_DISTRIBUTION_EXEC: $ALPINE_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier3:alpine:3.21@prep:
extends:
- .fdo.container-build@alpine
@ -324,6 +268,34 @@ tier3:alpine:3.19@prep:
when: manual
allow_failure: true
tier3:centos:stream10@prep:
extends:
- .fdo.container-build@centos
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'stream10'
FDO_DISTRIBUTION_TAG: $CENTOS_TAG
FDO_DISTRIBUTION_EXEC: $CENTOS_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
tier3:centos:stream9@prep:
extends:
- .fdo.container-build@centos
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: 'stream9'
FDO_DISTRIBUTION_TAG: $CENTOS_TAG
FDO_DISTRIBUTION_EXEC: $CENTOS_EXEC
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
when: manual
allow_failure: true
#################################################################
# #
# tierN stage #
@ -340,7 +312,7 @@ tier3:alpine:3.19@prep:
dependencies: []
t_fedora:42:
t_fedora:43:
extends:
- .build@template
- .fdo.distribution-image@fedora
@ -356,94 +328,24 @@ t_fedora:42:
- tarball
- subtree
variables:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "tier1:fedora:42@prep"
- "tier1:fedora:43@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_fedora:rawhide:
t_fedora:42:
extends:
- .build@template
- .fdo.distribution-image@fedora
- .nm_artifacts_debug
stage: tier2
stage: tier3
variables:
FDO_DISTRIBUTION_VERSION: 'rawhide'
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "tier2:fedora:rawhide@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_centos:stream9:
extends:
- .build@template
- .fdo.distribution-image@centos
- .nm_artifacts_debug
stage: tier2
variables:
FDO_DISTRIBUTION_VERSION: 'stream9'
FDO_DISTRIBUTION_TAG: $CENTOS_TAG
needs:
- "tier2:centos:stream9@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_ubuntu:devel:
extends:
- .build@template
- .fdo.distribution-image@ubuntu
- .nm_artifacts_debug
stage: tier2
variables:
FDO_DISTRIBUTION_VERSION: 'devel'
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
needs:
- "tier2:ubuntu:devel@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_debian:testing:
extends:
- .build@template
- .fdo.distribution-image@debian
- .nm_artifacts_debug
stage: tier2
variables:
FDO_DISTRIBUTION_VERSION: 'testing'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
needs:
- "tier2:debian:testing@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_debian:sid:
extends:
- .build@template
- .fdo.distribution-image@debian
- .nm_artifacts_debug
stage: tier2
variables:
FDO_DISTRIBUTION_VERSION: 'sid'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
needs:
- "tier2:debian:sid@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_alpine:edge:
extends:
- .build@template
- .fdo.distribution-image@alpine
- .nm_artifacts_debug
stage: tier2
variables:
FDO_DISTRIBUTION_VERSION: 'edge'
FDO_DISTRIBUTION_TAG: $ALPINE_TAG
needs:
- "tier2:alpine:edge@prep"
- "tier3:fedora:42@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
@ -475,20 +377,6 @@ t_ubuntu:25.04:
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_ubuntu:24.10:
extends:
- .build@template
- .fdo.distribution-image@ubuntu
- .nm_artifacts_debug
stage: tier3
variables:
FDO_DISTRIBUTION_VERSION: '24.10'
FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
needs:
- "tier3:ubuntu:24.10@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_ubuntu:24.04:
extends:
- .build@template
@ -517,6 +405,20 @@ t_ubuntu:22.04:
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_debian:13:
extends:
- .build@template
- .fdo.distribution-image@debian
- .nm_artifacts_debug
stage: tier3
variables:
FDO_DISTRIBUTION_VERSION: '13'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
needs:
- "tier3:debian:13@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_debian:12:
extends:
- .build@template
@ -531,6 +433,20 @@ t_debian:12:
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_alpine:3.22:
extends:
- .build@template
- .fdo.distribution-image@alpine
- .nm_artifacts_debug
stage: tier3
variables:
FDO_DISTRIBUTION_VERSION: '3.22'
FDO_DISTRIBUTION_TAG: $ALPINE_TAG
needs:
- "tier3:alpine:3.22@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_alpine:3.21:
extends:
- .build@template
@ -573,6 +489,34 @@ t_alpine:3.19:
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_centos:stream10:
extends:
- .build@template
- .fdo.distribution-image@centos
- .nm_artifacts_debug
stage: tier3
variables:
FDO_DISTRIBUTION_VERSION: 'stream10'
FDO_DISTRIBUTION_TAG: $CENTOS_TAG
needs:
- "tier3:centos:stream10@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
t_centos:stream9:
extends:
- .build@template
- .fdo.distribution-image@centos
- .nm_artifacts_debug
stage: tier3
variables:
FDO_DISTRIBUTION_VERSION: 'stream9'
FDO_DISTRIBUTION_TAG: $CENTOS_TAG
needs:
- "tier3:centos:stream9@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
#################################################################
# #
# specific jobs #
@ -583,10 +527,10 @@ check-patch:
extends:
- .fdo.distribution-image@fedora
variables:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "tier1:fedora:42@prep"
- "tier1:fedora:43@prep"
rules:
- if: $CI_PIPELINE_SOURCE != 'schedule'
stage: tier1
@ -598,10 +542,10 @@ check-tree:
extends:
- .fdo.distribution-image@fedora
variables:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "tier1:fedora:42@prep"
- "tier1:fedora:43@prep"
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event' && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH
allow_failure: true
@ -631,9 +575,9 @@ pages:
when: never
- if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == 'main'
dependencies:
- "t_fedora:42: [meson+gcc+docs+valgrind]"
- "t_fedora:43: [meson+gcc+docs+valgrind]"
needs:
- "t_fedora:42: [meson+gcc+docs+valgrind]"
- "t_fedora:43: [meson+gcc+docs+valgrind]"
triage:issues:
stage: triage
@ -650,11 +594,11 @@ coverity:
extends:
- .fdo.distribution-image@fedora
variables:
FDO_DISTRIBUTION_VERSION: '42'
FDO_DISTRIBUTION_VERSION: '43'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
stage: coverity
needs:
- "tier1:fedora:42@prep"
- "tier1:fedora:43@prep"
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULED_PIPELINE_NAME == "weekly"
script:

View file

@ -23,52 +23,38 @@ distributions:
- name: fedora
tier: 1
versions:
- '42'
- '43'
# TIER 2: distribution versions that will or might use the current NM version.
# Run when doing a release.
- name: fedora
tier: 2
versions:
- 'rawhide'
- name: centos
tier: 2
versions:
- 'stream9'
- name: ubuntu
tier: 2
versions:
- 'devel'
- name: debian
tier: 2
versions:
- 'testing'
- 'sid'
- name: alpine
tier: 2
versions:
- 'edge'
# TIER 3: distribution versions not in EOL but don't use the current NM version.
# Run when doing a release, but a failure won't be blocking for the release.
- name: fedora
tier: 3
versions:
- '42'
- '41'
- name: ubuntu
tier: 3
versions:
- '25.04'
- '24.10'
- '24.04'
- '22.04'
- name: debian
tier: 3
versions:
- '13'
- '12'
- name: alpine
tier: 3
versions:
- '3.22'
- '3.21'
- '3.20'
- '3.19'
- name: centos
tier: 3
versions:
- 'stream10'
- 'stream9'

View file

@ -13,6 +13,8 @@ if [ $IS_CENTOS = 1 ]; then
CENTOS_VERSION=8
elif grep -q '^VERSION_ID=.*\<9\>' /etc/os-release ; then
CENTOS_VERSION=9
elif grep -q '^VERSION_ID=.*\<10\>' /etc/os-release ; then
CENTOS_VERSION=10
else
exit 1
fi
@ -33,6 +35,11 @@ fi
dnf install -y 'dnf-command(config-manager)'
dnf config-manager --set-enabled crb
curl https://copr.fedorainfracloud.org/coprs/nmstate/nm-build-deps/repo/epel-9/nmstate-nm-build-deps-epel-9.repo > /etc/yum.repos.d/nmstate-nm-build-deps-epel-9.repo
elif [ "$CENTOS_VERSION" = stream10 ]; then
dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
dnf install -y 'dnf-command(config-manager)'
dnf config-manager --set-enabled crb
curl https://copr.fedorainfracloud.org/coprs/nmstate/nm-build-deps/repo/epel-10/nmstate-nm-build-deps-epel-10.repo > /etc/yum.repos.d/nmstate-nm-build-deps-epel-10.repo
else
exit 1
fi

View file

@ -17,10 +17,17 @@ grep -q '^NAME=.*\(CentOS\)' /etc/os-release && IS_CENTOS=1
grep -q '^NAME=.*\(Fedora\)' /etc/os-release && IS_FEDORA=1
grep -q '^NAME=.*\(Alpine\)' /etc/os-release && IS_ALPINE=1
IS_CENTOS_7=0
if [ $IS_CENTOS = 1 ]; then
if grep -q '^VERSION_ID=.*\<7\>' /etc/os-release ; then
IS_CENTOS_7=1
CENTOS_VER_LINE="$(grep '^VERSION_ID=' /etc/os-release)"
if [[ $CENTOS_VER_LINE =~ ^VERSION_ID=\"?([0-9]+)\"?$ ]]; then
CENTOS_VER="${BASH_REMATCH[1]}"
else
echo "Error detecting CentOS Stream version" >&2
exit 1
fi
if (( $CENTOS_VER >= 10 )); then
export WITH_LIBTEAM=0
fi
fi

34
NEWS
View file

@ -1,3 +1,37 @@
===============================================
NetworkManager-1.54.3
Overview of changes since NetworkManager-1.54.2
===============================================
* For private connections (the ones that specify a user in the
"connection.permissions" property), verify that the user can access
the 802.1X certificates and keys set in the connection.
* Introduce a libnm function that can be used by VPN plugins to check
user permissions on certificate and keys.
===============================================
NetworkManager-1.54.2
Overview of changes since NetworkManager-1.54.1
===============================================
* Support reapplying the "sriov.vfs" property as long as
"sriov.total-vfs" is not changed.
* Support configuring the HSR protocol version via the
"hsr.protocol-version" property.
* Support configuring the HSR interlink port via the
"hsr.interlink" property.
===============================================
NetworkManager-1.54.1
Overview of changes since NetworkManager-1.54.0
===============================================
* Make that global-dns configuration overwrites DNS searches and
options from connections, instead of merging all together.
* Add support for a new rd.net.dhcp.client-id option in
nm-initrd-generator.
* Minor bug fixes.
=============================================
NetworkManager-1.54
Overview of changes since NetworkManager-1.52

View file

@ -11,6 +11,7 @@ apk add \
'clang' \
'curl-dev' \
'dbus' \
'dbus-dev' \
'elogind-dev' \
'eudev-dev' \
'gcc' \

View file

@ -691,7 +691,7 @@ Preferably use nmcli instead.
-Dsession_tracking=systemd \
-Dsuspend_resume=systemd \
-Dsystemdsystemunitdir=%{_unitdir} \
-Dsystem_ca_path=/etc/pki/tls/cert.pem \
-Dsystem_ca_path=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \
-Ddbus_conf_dir=%{dbus_sys_dir} \
-Dtests=yes \
-Dvalgrind=no \
@ -896,6 +896,7 @@ fi
%{_libexecdir}/nm-dispatcher
%{_libexecdir}/nm-initrd-generator
%{_libexecdir}/nm-daemon-helper
%{_libexecdir}/nm-libnm-helper
%{_libexecdir}/nm-priv-helper
%dir %{_libdir}/%{name}
%dir %{nmplugindir}

View file

@ -84,6 +84,14 @@ if [ "$CC" != gcc ]; then
_WITH_CRYPTO=nss
fi
if [ "$WITH_LIBTEAM" != "" ]; then
if _is_true "$WITH_LIBTEAM"; then
_WITH_LIBTEAM="true"
else
_WITH_LIBTEAM="false"
fi
fi
if [ "$WITH_DOCS" != "" ]; then
if _is_true "$WITH_DOCS"; then
_WITH_DOCS="true"

View file

@ -23,8 +23,8 @@ static const char *
nm_state_to_string(NMState state)
{
switch (state) {
case NM_STATE_ASLEEP:
return "asleep";
case NM_STATE_DISABLED:
return "network off";
case NM_STATE_CONNECTING:
return "connecting";
case NM_STATE_CONNECTED_LOCAL:

View file

@ -1540,8 +1540,12 @@ managed=1
<refsect1>
<title><literal>global-dns</literal> section</title>
<para>This section specifies DNS settings that are applied
globally, in addition to connection-specific ones.</para>
<para>This section specifies DNS settings that are applied globally. They
override the equivalent options defined in individual connections, making
them to be ignored. If a [global-dns-domain-*] section is defined, but this
section isn't, an empty [global-dns] section is assumed, thus overwriting
connection specific configurations too.
</para>
<para>
<variablelist>
<varlistentry>
@ -1601,6 +1605,10 @@ managed=1
default domain "*". When the global DNS domains are valid, the
name servers and domains defined globally override the ones from
active connections.
If any global DNS domain is defined but a [global-dns] section isn't,
an empty [global-dns] section is assumed, thus overwriting its
connection specific configurations too.
</para>
<para>
<variablelist>

View file

@ -162,6 +162,7 @@
<member><option>rd.net.dns-backend</option></member>
<member><option>rd.net.dns-resolve-mode</option></member>
<member><option>rd.net.timeout.dhcp</option></member>
<member><option>rd.net.dhcp.client-id</option></member>
<member><option>rd.net.dhcp.retry</option></member>
<member><option>rd.net.dhcp.vendor-class</option></member>
<member><option>rd.net.dhcp.dscp</option></member>
@ -268,6 +269,23 @@
</para>
</listitem>
<listitem>
<para>NetworkManager supports the
<option>rd.net.dhcp.client-id</option>=<replaceable>interface</replaceable>:<replaceable>client-id</replaceable>
kernel command line option to set a specific DHCPv4 client identifier
for the given interface. The client-id can be specified either as a
sequence of bytes in hexadecimal format separated by dashes, or as the
character '@' followed by a non-empty string. When using the second
format, NetworkManager prepends a zero byte to the given string,
according to section 9.14 of RFC 2132. See the "ipv4.dhcp-client-id"
section of <link
linkend='nm-settings-nmcli'><citerefentry><refentrytitle>nm-settings-nmcli</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>
for more details. Examples:
<literal>rd.net.dhcp.client-id=eth0:01-52-54-00-45-87-42</literal>,
<literal>rd.net.dhcp.client-id=enp1s0:@example.com</literal>.
</para>
</listitem>
</itemizedlist>
</refsect1>
@ -278,6 +296,7 @@
<refsect1 id='see_also'><title>See Also</title>
<para><link linkend='dracut.cmdline'><citerefentry><refentrytitle>dracut.cmdline</refentrytitle><manvolnum>7</manvolnum></citerefentry></link>,
<link linkend='NetworkManager'><citerefentry><refentrytitle>NetworkManager</refentrytitle><manvolnum>8</manvolnum></citerefentry></link>.</para>
<link linkend='NetworkManager'><citerefentry><refentrytitle>NetworkManager</refentrytitle><manvolnum>8</manvolnum></citerefentry></link>,
<link linkend='nm-settings-nmcli'><citerefentry><refentrytitle>nm-settings-nmcli</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>.</para>
</refsect1>
</refentry>

View file

@ -91,6 +91,10 @@
NetworkManager inserts the records for Bridges into OVSDB when a Port is
attached.
</para>
<para>Known limitation: when the last NetworkManager's owned port is removed,
the bridge is removed too, even if there are other externally attached ports.
</para>
</refsect2>
<refsect2>
@ -102,6 +106,10 @@
exist. Ports can also be configured to do VLAN tagging or Bonding.
NetworkManager inserts the records for Ports into OVSDB when an Interface is
attached. Ports must be attached to a Bridge.</para>
<para>Known limitation: when the last NetworkManager's owned interface is removed,
the port is removed too, even if there are other externally attached interfaces.
</para>
</refsect2>
<refsect2>

View file

@ -5,7 +5,7 @@ project(
# NOTE: When incrementing version also add corresponding
# NM_VERSION_x_y_z macros in
# "src/libnm-core-public/nm-version-macros.h.in"
version: '1.54.0',
version: '1.54.3',
license: 'GPL2+',
default_options: [
'buildtype=debugoptimized',

View file

@ -52,8 +52,7 @@
NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \
NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_USE_CARRIER, \
NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, \
NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX, \
NM_SETTING_BOND_OPTION_LACP_ACTIVE
NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX
#define OPTIONS_REAPPLY_FULL \
OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \

View file

@ -630,10 +630,17 @@ build_supplicant_config(NMDeviceEthernet *self, GError **error)
mtu = nm_platform_link_get_mtu(nm_device_get_platform(NM_DEVICE(self)),
nm_device_get_ifindex(NM_DEVICE(self)));
config = nm_supplicant_config_new(NM_SUPPL_CAP_MASK_NONE);
config = nm_supplicant_config_new(NM_SUPPL_CAP_MASK_NONE,
nm_utils_get_connection_first_permissions_user(connection));
security = nm_connection_get_setting_802_1x(connection);
if (!nm_supplicant_config_add_setting_8021x(config, security, con_uuid, mtu, TRUE, error)) {
if (!nm_supplicant_config_add_setting_8021x(config,
security,
con_uuid,
mtu,
TRUE,
nm_device_get_private_files(NM_DEVICE(self)),
error)) {
g_prefix_error(error, "802-1x-setting: ");
g_clear_object(&config);
}

View file

@ -117,28 +117,50 @@ create_and_realize(NMDevice *device,
GError **error)
{
const char *iface = nm_device_get_iface(device);
nm_auto_free char *err_msg = NULL;
NMSettingHsr *s_hsr;
NMPlatformLnkHsr lnk = {};
int r;
int r = 0;
s_hsr = _nm_connection_get_setting(connection, NM_TYPE_SETTING_HSR);
nm_assert(s_hsr);
if (nm_setting_hsr_get_port1(s_hsr) != NULL)
lnk.port1 = nm_platform_link_get_ifindex(NM_PLATFORM_GET, nm_setting_hsr_get_port1(s_hsr));
if (nm_setting_hsr_get_port2(s_hsr) != NULL)
lnk.port2 = nm_platform_link_get_ifindex(NM_PLATFORM_GET, nm_setting_hsr_get_port2(s_hsr));
if (nm_setting_hsr_get_interlink(s_hsr) != NULL) {
const char *ifname = nm_setting_hsr_get_interlink(s_hsr);
int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, ifname);
if (ifindex <= 0) {
err_msg = g_strdup_printf("interlink port '%s' does not exist", ifname);
goto out;
}
lnk.interlink = ifindex;
}
lnk.multicast_spec = nm_setting_hsr_get_multicast_spec(s_hsr);
lnk.prp = nm_setting_hsr_get_prp(s_hsr);
lnk.protocol_version = nm_setting_hsr_get_protocol_version(s_hsr);
r = nm_platform_link_hsr_add(nm_device_get_platform(device), iface, &lnk, out_plink);
if (r < 0) {
err_msg = g_strdup(nm_strerror(r) ?: "unknown");
}
out:
if (err_msg) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_CREATION_FAILED,
"Failed to create HSR interface '%s' for '%s': %s",
iface,
nm_connection_get_id(connection),
nm_strerror(r));
err_msg);
return FALSE;
}

View file

@ -201,7 +201,8 @@ build_supplicant_config(NMDeviceMacsec *self, GError **error)
mtu = nm_platform_link_get_mtu(nm_device_get_platform(NM_DEVICE(self)),
nm_device_get_ifindex(NM_DEVICE(self)));
config = nm_supplicant_config_new(NM_SUPPL_CAP_MASK_NONE);
config = nm_supplicant_config_new(NM_SUPPL_CAP_MASK_NONE,
nm_utils_get_connection_first_permissions_user(connection));
s_macsec = nm_device_get_applied_setting(NM_DEVICE(self), NM_TYPE_SETTING_MACSEC);
@ -227,7 +228,13 @@ build_supplicant_config(NMDeviceMacsec *self, GError **error)
if (nm_setting_macsec_get_mode(s_macsec) == NM_SETTING_MACSEC_MODE_EAP) {
s_8021x = nm_connection_get_setting_802_1x(connection);
if (!nm_supplicant_config_add_setting_8021x(config, s_8021x, con_uuid, mtu, TRUE, error)) {
if (!nm_supplicant_config_add_setting_8021x(config,
s_8021x,
con_uuid,
mtu,
TRUE,
nm_device_get_private_files(NM_DEVICE(self)),
error)) {
g_prefix_error(error, "802-1x-setting: ");
return NULL;
}

View file

@ -179,4 +179,6 @@ void nm_device_auth_request(NMDevice *self,
void nm_device_link_properties_set(NMDevice *self, gboolean reapply);
GHashTable *nm_device_get_private_files(NMDevice *self);
#endif /* NM_DEVICE_PRIVATE_H */

View file

@ -135,13 +135,15 @@ NM_UTILS_LOOKUP_STR_DEFINE(
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT,
"unmanaged-link-not-init"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING, "unmanaged-quitting"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING, "unmanaged-sleeping"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED,
"unmanaged-nm-disabled"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF, "unmanaged-user-conf"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT,
"unmanaged-user-explicit"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS,
"unmanaged-user-settings"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"), );
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NETWORKING_OFF, "networking-off"), );
NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string,
NMDeviceMtuSource,
@ -235,7 +237,7 @@ resolve_addr_helper_cb(GObject *source, GAsyncResult *result, gpointer user_data
gs_free_error GError *error = NULL;
gs_free char *output = NULL;
output = nm_utils_spawn_helper_finish(result, &error);
output = nm_utils_spawn_helper_finish_string(result, &error);
if (nm_utils_error_is_cancelled(error))
return;
@ -274,6 +276,7 @@ resolve_addr_spawn_helper(ResolveAddrInfo *info, ResolveAddrService services)
nm_inet_ntop(info->addr_family, &info->address, addr_str);
_LOG2D(info, "start lookup via nm-daemon-helper using services: %s", str);
nm_utils_spawn_helper(NM_MAKE_STRV("resolve-address", addr_str, str),
FALSE,
g_task_get_cancellable(info->task),
resolve_addr_helper_cb,
info);

View file

@ -335,6 +335,12 @@ typedef struct {
int addr_family;
} HostnameResolver;
typedef enum {
PRIVATE_FILES_STATE_UNKNOWN = 0,
PRIVATE_FILES_STATE_READING,
PRIVATE_FILES_STATE_DONE,
} PrivateFilesState;
/*****************************************************************************/
enum {
@ -692,6 +698,8 @@ typedef struct _NMDevicePrivate {
IPDevStateData ipdev_data_unspec;
gulong sharing_ipv4_changed_id;
struct {
/* If we set the addrgenmode6, this records the previously set value. */
guint8 previous_mode_val;
@ -769,6 +777,13 @@ typedef struct _NMDevicePrivate {
guint64 rx_bytes;
} stats;
struct {
GHashTable *table;
GCancellable *cancellable;
char *user;
PrivateFilesState state;
} private_files;
bool mtu_force_set_done : 1;
bool needs_ip6_subnet : 1;
@ -779,7 +794,6 @@ typedef struct _NMDevicePrivate {
char *prop_ip_iface; /* IP interface D-Bus property */
GList *ping_operations;
GSource *ping_timeout;
bool refresh_forwarding_done : 1;
} NMDevicePrivate;
G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT)
@ -867,6 +881,8 @@ static void _dev_ipshared4_spawn_dnsmasq(NMDevice *self);
static void _dev_ipshared6_start(NMDevice *self);
static void _dev_ipforwarding4_start(NMDevice *self, int addr_family);
static void
_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean preserve_dhcp);
@ -2131,8 +2147,8 @@ _prop_get_ipvx_dhcp_send_hostname(NMDevice *self, int addr_family)
return send_hostname_v2;
}
NMSettingIPConfigForwarding
nm_device_get_ipv4_forwarding(NMDevice *self)
static NMSettingIPConfigForwarding
_prop_get_ipv4_forwarding(NMDevice *self)
{
NMSettingIPConfig *s_ip;
NMSettingIPConfigForwarding forwarding;
@ -3778,7 +3794,7 @@ nm_device_assume_state_reset(NMDevice *self)
/*****************************************************************************/
char *
static char *
nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property)
{
const char *ifname;
@ -6645,7 +6661,7 @@ concheck_update_state(NMDevice *self,
}
}
const char *
static const char *
nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family)
{
NMDeviceClass *klass;
@ -8639,6 +8655,8 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error)
g_object_thaw_notify(G_OBJECT(self));
nm_device_managed_type_set(self, NM_DEVICE_MANAGED_TYPE_REMOVED);
nm_device_set_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
nm_device_set_unmanaged_flags(self,
@ -9164,6 +9182,10 @@ is_available(NMDevice *self, NMDeviceCheckDevAvailableFlags flags)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
/* unrealized software devices are always available, hardware devices never */
if (!nm_device_is_real(self))
return nm_device_is_software(self);
if (priv->carrier || priv->ignore_carrier)
return TRUE;
@ -10390,6 +10412,43 @@ sriov_params_cb(GError *error, gpointer user_data)
nm_device_activate_schedule_stage1_device_prepare(self, FALSE);
}
static gboolean
sriov_gen_platform_vfs(NMDevice *self,
NMSettingSriov *s_sriov,
NMPlatformVF ***plat_vfs_out,
GError **error)
{
nm_auto_freev NMPlatformVF **plat_vfs = NULL;
guint num;
nm_assert(s_sriov);
nm_assert(plat_vfs_out && !*plat_vfs_out);
num = nm_setting_sriov_get_num_vfs(s_sriov);
plat_vfs = g_new0(NMPlatformVF *, num + 1);
for (int i = 0; i < num; i++) {
NMSriovVF *vf = nm_setting_sriov_get_vf(s_sriov, i);
gs_free_error GError *local = NULL;
plat_vfs[i] = sriov_vf_config_to_platform(self, vf, &local);
if (!plat_vfs[i]) {
g_set_error(error,
local->domain,
local->code,
"VF '%s' is invalid: %s",
nm_utils_sriov_vf_to_str(vf, FALSE, NULL),
local->message);
return FALSE;
}
}
*plat_vfs_out = g_steal_pointer(&plat_vfs);
return TRUE;
}
/*
* activate_stage1_device_prepare
*
@ -10436,10 +10495,7 @@ activate_stage1_device_prepare(NMDevice *self)
if (s_sriov && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) {
nm_auto_freev NMPlatformVF **plat_vfs = NULL;
gs_free_error GError *error = NULL;
NMSriovVF *vf;
NMTernary autoprobe;
guint num;
guint i;
autoprobe = nm_setting_sriov_get_autoprobe_drivers(s_sriov);
if (autoprobe == NM_TERNARY_DEFAULT) {
@ -10452,22 +10508,13 @@ activate_stage1_device_prepare(NMDevice *self)
NM_OPTION_BOOL_TRUE);
}
num = nm_setting_sriov_get_num_vfs(s_sriov);
plat_vfs = g_new0(NMPlatformVF *, num + 1);
for (i = 0; i < num; i++) {
vf = nm_setting_sriov_get_vf(s_sriov, i);
plat_vfs[i] = sriov_vf_config_to_platform(self, vf, &error);
if (!plat_vfs[i]) {
_LOGE(LOGD_DEVICE,
"failed to apply SR-IOV VF '%s': %s",
nm_utils_sriov_vf_to_str(vf, FALSE, NULL),
error->message);
if (!sriov_gen_platform_vfs(self, s_sriov, &plat_vfs, &error)) {
_LOGE(LOGD_DEVICE, "cannot parse the VF list: %s", error->message);
nm_device_state_changed(self,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
return;
}
}
/* When changing the number of VFs the kernel can block
* for very long time in the write to sysfs, especially
@ -10773,6 +10820,49 @@ tc_commit(NMDevice *self)
return TRUE;
}
static void
read_private_files_cb(GObject *source_object, GAsyncResult *result, gpointer data)
{
gs_unref_hashtable GHashTable *table = NULL;
gs_free_error GError *error = NULL;
NMDevice *self;
NMDevicePrivate *priv;
table = nm_utils_read_private_files_finish(result, &error);
if (nm_utils_error_is_cancelled(error))
return;
self = NM_DEVICE(data);
priv = NM_DEVICE_GET_PRIVATE(self);
if (error) {
NMConnection *connection = nm_device_get_applied_connection(self);
_LOGW(LOGD_DEVICE,
"could not read files for private connection %s owned by user '%s': %s",
connection ? nm_connection_get_uuid(connection) : NULL,
priv->private_files.user,
error->message);
nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
return;
}
_LOGD(LOGD_DEVICE, "private files successfully read");
priv->private_files.state = PRIVATE_FILES_STATE_DONE;
priv->private_files.table = g_steal_pointer(&table);
g_clear_pointer(&priv->private_files.user, g_free);
g_clear_object(&priv->private_files.cancellable);
nm_device_activate_schedule_stage2_device_config(self, FALSE);
}
GHashTable *
nm_device_get_private_files(NMDevice *self)
{
return NM_DEVICE_GET_PRIVATE(self)->private_files.table;
}
/*
* activate_stage2_device_config
*
@ -10785,6 +10875,7 @@ activate_stage2_device_config(NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self);
NMConnection *applied;
NMActStageReturn ret;
NMSettingWired *s_wired;
gboolean no_firmware = FALSE;
@ -10793,6 +10884,68 @@ activate_stage2_device_config(NMDevice *self)
nm_device_state_changed(self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
applied = nm_device_get_applied_connection(self);
/* If the connection is private (owned by a specific user), we need to
* verify that the user has permission to access any files specified in
* the connection, such as certificates and keys. We do that by calling
* nm_utils_read_private_files() and saving the file contents in a hash
* table that can be accessed later during the activation. It is important
* to never access the files again to avoid TOCTOU bugs.
*/
switch (priv->private_files.state) {
case PRIVATE_FILES_STATE_UNKNOWN:
{
gs_free const char **paths = NULL;
NMSettingConnection *s_con;
const char *user;
s_con = nm_connection_get_setting_connection(applied);
nm_assert(s_con);
user = _nm_setting_connection_get_first_permissions_user(s_con);
priv->private_files.user = g_strdup(user);
if (!priv->private_files.user) {
priv->private_files.state = PRIVATE_FILES_STATE_DONE;
break;
}
paths = nm_utils_get_connection_private_files_paths(applied);
if (!paths) {
priv->private_files.state = PRIVATE_FILES_STATE_DONE;
break;
}
if (_nm_setting_connection_get_num_permissions_users(s_con) > 1) {
_LOGW(LOGD_DEVICE,
"private connections with multiple users are not allowed to reference "
"certificates and keys on the filesystem. Specify only one user in the "
"connection.permissions property.");
nm_device_state_changed(self,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_CONFIG_FAILED);
return;
}
priv->private_files.state = PRIVATE_FILES_STATE_READING;
priv->private_files.cancellable = g_cancellable_new();
_LOGD(LOGD_DEVICE, "reading private files");
nm_utils_read_private_files(paths,
priv->private_files.user,
priv->private_files.cancellable,
read_private_files_cb,
self);
return;
}
case PRIVATE_FILES_STATE_READING:
/* wait */
return;
case PRIVATE_FILES_STATE_DONE:
/* proceed */
break;
}
if (!nm_device_managed_type_is_external(self)) {
_ethtool_state_set(self);
nm_device_link_properties_set(self, FALSE);
@ -10809,7 +10962,7 @@ activate_stage2_device_config(NMDevice *self)
priv->tc_committed = TRUE;
}
nm_routing_rules_sync(nm_device_get_applied_connection(self),
nm_routing_rules_sync(applied,
NM_TERNARY_TRUE,
klass->get_extra_rules,
self,
@ -13135,17 +13288,12 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family)
goto out_devip;
if (IS_IPv4) {
NMSettingIPConfigForwarding ipv4_forwarding = nm_device_get_ipv4_forwarding(self);
if (NM_IN_SET(ipv4_forwarding,
NM_SETTING_IP_CONFIG_FORWARDING_NO,
NM_SETTING_IP_CONFIG_FORWARDING_YES)) {
nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", ipv4_forwarding ? "1" : "0");
}
priv->ipll_data_4.v4.mode = _prop_get_ipv4_link_local(self);
if (priv->ipll_data_4.v4.mode == NM_SETTING_IP4_LL_ENABLED)
_dev_ipll4_start(self);
_dev_ipforwarding4_start(self, addr_family);
if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
_dev_ipdhcpx_start(self, AF_INET);
else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
@ -13467,13 +13615,19 @@ _dev_ipsharedx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
const int IS_IPv4 = NM_IS_IPv4(addr_family);
NMDeviceIPState old_state = priv->ipshared_data_x[IS_IPv4].state;
if (priv->ipshared_data_x[IS_IPv4].state != state) {
if (old_state != state) {
_LOGD_ipshared(addr_family,
"set state %s (was %s)",
nm_device_ip_state_to_string(state),
nm_device_ip_state_to_string(priv->ipshared_data_x[IS_IPv4].state));
nm_device_ip_state_to_string(old_state));
priv->ipshared_data_x[IS_IPv4].state = state;
if (old_state == NM_DEVICE_IP_STATE_READY || state == NM_DEVICE_IP_STATE_READY)
nm_manager_update_shared_connection(NM_MANAGER_GET,
addr_family,
state == NM_DEVICE_IP_STATE_READY);
}
}
@ -13768,6 +13922,106 @@ _dev_ipshared6_start(NMDevice *self)
/*****************************************************************************/
/**
* Set the device's forwarding to the specified value. If %NM_TERNARY_DEFAULT is specified,
* it's set to the kernel's default, otherwise it's set to the specific value.
*/
static void
_dev_ipforwarding4_set(NMDevice *self, NMTernary val)
{
gs_free const char *default_forwarding = NULL;
gs_free const char *current_forwarding = NULL;
const char *val_str;
if (val != NM_TERNARY_DEFAULT) {
val_str = val ? "1" : "0";
} else {
default_forwarding = nm_platform_sysctl_get(
nm_device_get_platform(self),
NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"));
if (!default_forwarding) {
_LOGW(LOGD_DEVICE,
"error setting IPv4 forwarding: can't read default forwarding value: %s",
nm_strerror_native(errno));
return; /* Non fatal */
}
val_str = default_forwarding;
}
current_forwarding = nm_device_sysctl_ip_conf_get(self, AF_INET, "forwarding");
if (nm_streq0(current_forwarding, val_str))
return;
if (!nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", val_str))
_LOGW(LOGD_DEVICE,
"error setting IPv4 forwarding to '%s': %s",
val_str,
nm_strerror_native(errno));
}
static void
_dev_ipforwarding4_auto_cb(NMManager *manager, gboolean sharing_ipv4, gpointer data)
{
NMDevice *self = NM_DEVICE(data);
_dev_ipforwarding4_set(self, sharing_ipv4 ? NM_TERNARY_TRUE : NM_TERNARY_DEFAULT);
}
static void
_dev_ipforwarding4_start(NMDevice *self, int addr_family)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
NMSettingIPConfigForwarding ipv4_forwarding = _prop_get_ipv4_forwarding(self);
NMTernary new_forwarding = NM_TERNARY_DEFAULT;
/* IPv6 per-interface forwarding not supported yet */
if (addr_family != AF_INET)
return;
if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
new_forwarding = NM_TERNARY_TRUE;
} else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_YES) {
new_forwarding = NM_TERNARY_TRUE;
} else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_NO) {
new_forwarding = NM_TERNARY_FALSE;
} else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_AUTO) {
if (nm_manager_get_sharing_ipv4(NM_MANAGER_GET))
new_forwarding = NM_TERNARY_TRUE;
else
new_forwarding = NM_TERNARY_DEFAULT;
if (!priv->sharing_ipv4_changed_id)
priv->sharing_ipv4_changed_id = g_signal_connect(NM_MANAGER_GET,
NM_MANAGER_SHARING_IPV4_CHANGED,
G_CALLBACK(_dev_ipforwarding4_auto_cb),
self);
} else {
nm_assert_not_reached();
}
_dev_ipforwarding4_set(self, new_forwarding);
}
static void
_dev_ipforwarding_cleanup(NMDevice *self, int addr_family, CleanupType cleanup_type)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
if (!NM_IS_IPv4(addr_family))
return;
nm_clear_g_signal_handler(NM_MANAGER_GET, &priv->sharing_ipv4_changed_id);
if (NM_IN_SET(cleanup_type, CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP_REAPPLY)) {
/* Deconfigure by restoring kernel's default */
_dev_ipforwarding4_set(self, NM_TERNARY_DEFAULT);
}
}
/*****************************************************************************/
static void
act_request_set(NMDevice *self, NMActRequest *act_request)
{
@ -13880,6 +14134,8 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
gboolean keep_reapply = (cleanup_type == CLEANUP_TYPE_KEEP_REAPPLY);
_dev_ipforwarding_cleanup(self, addr_family, cleanup_type);
_dev_ipsharedx_cleanup(self, addr_family);
_dev_ipdev_cleanup(self, AF_UNSPEC);
@ -14062,7 +14318,8 @@ can_reapply_change(NMDevice *self,
return nm_device_hash_check_invalid_keys(diffs,
NM_SETTING_SRIOV_SETTING_NAME,
error,
NM_SETTING_SRIOV_PRESERVE_ON_DOWN);
NM_SETTING_SRIOV_PRESERVE_ON_DOWN,
NM_SETTING_SRIOV_VFS);
}
out_fail:
@ -14240,9 +14497,35 @@ check_and_reapply_connection(NMDevice *self,
nm_device_link_properties_set(self, TRUE);
if (priv->state >= NM_DEVICE_STATE_CONFIG)
if (priv->state >= NM_DEVICE_STATE_CONFIG) {
GHashTable *sriov_diff;
lldp_setup(self, NM_TERNARY_DEFAULT);
sriov_diff = nm_g_hash_table_lookup(diffs, NM_SETTING_SRIOV_SETTING_NAME);
if (sriov_diff && nm_g_hash_table_lookup(sriov_diff, NM_SETTING_SRIOV_VFS)) {
nm_auto_freev NMPlatformVF **plat_vfs = NULL;
NMSettingSriov *s_sriov;
s_sriov = (NMSettingSriov *) nm_connection_get_setting(applied, NM_TYPE_SETTING_SRIOV);
if (s_sriov) {
gs_free_error GError *local = NULL;
if (!sriov_gen_platform_vfs(self, s_sriov, &plat_vfs, &local)
|| !nm_platform_link_set_sriov_vfs(nm_device_get_platform(self),
priv->ifindex,
(const NMPlatformVF *const *) plat_vfs)) {
_LOGE(LOGD_DEVICE,
"failed to reapply SRIOV VFs%s%s",
local ? ": " : "",
local ? local->message : "");
}
}
}
}
if (priv->state >= NM_DEVICE_STATE_IP_CONFIG) {
/* Allow reapply of MTU */
priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
@ -14696,13 +14979,15 @@ impl_device_delete(NMDBusObject *obj,
NULL);
}
static void
static gboolean
_device_activate(NMDevice *self, NMActRequest *req)
{
NMConnection *connection;
g_return_if_fail(NM_IS_DEVICE(self));
g_return_if_fail(NM_IS_ACT_REQUEST(req));
/* Returns TRUE on success, FALSE if the activation request could not be started */
g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE);
nm_assert(nm_device_is_real(self));
/* Ensure the activation request is still valid; the controller may have
@ -14710,7 +14995,7 @@ _device_activate(NMDevice *self, NMActRequest *req)
*/
if (nm_active_connection_get_state(NM_ACTIVE_CONNECTION(req))
>= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)
return;
return FALSE;
if (!nm_device_get_managed(self, FALSE)) {
/* It's unclear why the device would be unmanaged at this point.
@ -14721,7 +15006,7 @@ _device_activate(NMDevice *self, NMActRequest *req)
nm_active_connection_set_state_fail((NMActiveConnection *) req,
NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
NULL);
return;
return FALSE;
}
connection = nm_act_request_get_applied_connection(req);
@ -14737,6 +15022,8 @@ _device_activate(NMDevice *self, NMActRequest *req)
act_request_set(self, req);
nm_device_activate_schedule_stage1_device_prepare(self, FALSE);
return TRUE;
}
static void
@ -14756,7 +15043,9 @@ _carrier_wait_check_queued_act_request(NMDevice *self)
_LOGD(LOGD_DEVICE, "Activate queued activation request as we now have carrier");
queued_req = g_steal_pointer(&priv->queued_act_request);
_device_activate(self, queued_req);
if (!_device_activate(self, queued_req)) {
delete_on_deactivate_check_and_schedule(self);
}
}
}
@ -15603,7 +15892,7 @@ nm_device_get_firmware_missing(NMDevice *self)
NM_UTILS_FLAGS2STR_DEFINE(nm_unmanaged_flags2str,
NMUnmanagedFlags,
NM_UTILS_FLAGS2STR(NM_UNMANAGED_SLEEPING, "sleeping"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_MANAGER_DISABLED, "nm-disabled"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_QUITTING, "quitting"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_PLATFORM_INIT, "platform-init"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_USER_EXPLICIT, "user-explicit"),
@ -15667,8 +15956,8 @@ unmanaged_flags_to_reason(NMUnmanagedFlags flags)
/* Even if there are multiple flags, we can only return one reason.
* Return the most important reason.
*/
if (NM_FLAGS_HAS(flags, NM_UNMANAGED_SLEEPING))
return NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING;
if (NM_FLAGS_HAS(flags, NM_UNMANAGED_MANAGER_DISABLED))
return NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED;
if (NM_FLAGS_HAS(flags, NM_UNMANAGED_QUITTING))
return NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING;
if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_SETTINGS))
@ -16913,8 +17202,6 @@ _cleanup_generic_post(NMDevice *self, NMDeviceStateReason reason, CleanupType cl
priv->v4_route_table_all_sync_before = FALSE;
priv->v6_route_table_all_sync_before = FALSE;
priv->refresh_forwarding_done = FALSE;
priv->mtu_force_set_done = FALSE;
priv->needs_ip6_subnet = FALSE;
@ -16960,7 +17247,6 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
NMDevicePrivate *priv;
NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self);
int ifindex;
gint32 default_forwarding_v4;
g_return_if_fail(NM_IS_DEVICE(self));
@ -16983,21 +17269,16 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0");
}
/* Restoring the device's forwarding to the sysctl default is necessary because
* `refresh_forwarding()` only updates forwarding on activated devices. */
default_forwarding_v4 = nm_platform_sysctl_get_int32(
nm_device_get_platform(self),
NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"),
0);
nm_device_sysctl_ip_conf_set(self,
AF_INET,
"forwarding",
default_forwarding_v4 == 1 ? "1" : "0");
/* Call device type-specific deactivation */
if (klass->deactivate)
klass->deactivate(self);
/* Clean up private files */
nm_clear_g_cancellable(&priv->private_files.cancellable);
g_clear_pointer(&priv->private_files.table, g_hash_table_unref);
g_clear_pointer(&priv->private_files.user, g_free);
priv->private_files.state = PRIVATE_FILES_STATE_UNKNOWN;
ifindex = nm_device_get_ip_ifindex(self);
if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
@ -17534,7 +17815,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
gs_unref_object NMActRequest *queued_req = NULL;
queued_req = g_steal_pointer(&priv->queued_act_request);
_device_activate(self, queued_req);
if (!_device_activate(self, queued_req))
delete_on_deactivate_check_and_schedule(self);
}
break;
case NM_DEVICE_STATE_ACTIVATED:
@ -18936,19 +19218,6 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
return nm_assert_unreachable_val(NULL);
}
gboolean
nm_device_get_refresh_forwarding_done(NMDevice *self)
{
return NM_DEVICE_GET_PRIVATE(self)->refresh_forwarding_done;
}
void
nm_device_set_refresh_forwarding_done(NMDevice *self, gboolean is_refresh_forwarding_done)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
priv->refresh_forwarding_done = is_refresh_forwarding_done;
}
/*****************************************************************************/
static const char *

View file

@ -581,7 +581,8 @@ void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device);
/**
* NMUnmanagedFlags:
* @NM_UNMANAGED_NONE: placeholder value
* @NM_UNMANAGED_SLEEPING: %TRUE when unmanaged because NM is sleeping.
* @NM_UNMANAGED_MANAGER_DISABLED: %TRUE when unmanaged because NM is disabled.
* Currently, this happens when sleeping or with networking disabled.
* @NM_UNMANAGED_QUITTING: %TRUE when unmanaged because NM is shutting down.
* @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not
* yet initialized. Unrealized device are also unmanaged for this reason.
@ -610,7 +611,7 @@ typedef enum {
/* these flags are authoritative. If one of them is set,
* the device cannot be managed. */
NM_UNMANAGED_SLEEPING = (1LL << 0),
NM_UNMANAGED_MANAGER_DISABLED = (1LL << 0),
NM_UNMANAGED_QUITTING = (1LL << 1),
NM_UNMANAGED_PLATFORM_INIT = (1LL << 2),
NM_UNMANAGED_USER_EXPLICIT = (1LL << 3),
@ -852,14 +853,4 @@ void nm_routing_rules_sync(NMConnection *applied_connection,
NMDevice *self,
NMNetns *netns);
NMSettingIPConfigForwarding nm_device_get_ipv4_forwarding(NMDevice *self);
const char *nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family);
char *nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property);
gboolean nm_device_get_refresh_forwarding_done(NMDevice *self);
void nm_device_set_refresh_forwarding_done(NMDevice *self, gboolean is_refresh_forwarding_done);
#endif /* __NETWORKMANAGER_DEVICE_H__ */

View file

@ -1460,40 +1460,42 @@ _delete_interface(NMOvsdb *self, json_t *params, const char *ifname)
json_array_append_new(new_interfaces, json_pack("[s,s]", "uuid", interface_uuid));
}
if (num_nm_interfaces == 0) {
/* The port no longer has any NM interface. Don't add it to "new_ports" and set
* ports_changed=TRUE, so that it will be deleted. */
if (interfaces_changed && num_nm_interfaces == 0) {
/* We are deleting the last nm-interface of this port. Don't add it to "new_ports"
* and set ports_changed=TRUE, so that it will be deleted. */
ports_changed = TRUE;
} else {
if (interfaces_changed) {
/* An interface needs to be deleted from this port */
_expect_port_interfaces(params, ovs_port->name, interfaces);
_set_port_interfaces(params, ovs_port->name, new_interfaces);
}
/* The port is still alive */
/* Keep this port: it's still alive, or it's unrelated to the deleted interface */
json_array_append_new(new_ports, json_pack("[s,s]", "uuid", port_uuid));
if (ovs_port->connection_uuid)
num_nm_ports++;
if (interfaces_changed) {
/* This port is still alive, but an interface needs to be deleted from it */
_expect_port_interfaces(params, ovs_port->name, interfaces);
_set_port_interfaces(params, ovs_port->name, new_interfaces);
}
}
}
if (num_nm_ports == 0) {
/* The bridge no longer has any NM port. Don't add it to "new_bridges" and set
* bridges_changed=TRUE, so that it will be deleted. */
if (ports_changed && num_nm_ports == 0) {
/* We are deleting the last nm-port of this bridge. Don't add it to "new_bridges"
* and set bridges_changed=TRUE, so that it will be deleted. */
bridges_changed = TRUE;
} else {
/* Keep this bridge: it's still alive, or it's unrelated to the deleted interface */
json_array_append_new(new_bridges, json_pack("[s,s]", "uuid", ovs_bridge->bridge_uuid));
if (ports_changed) {
/* A port needs to be deleted from this bridge */
/* This bridge is still alive, but a port needs to be deleted from it */
_expect_bridge_ports(params, ovs_bridge->name, ports);
_set_bridge_ports(params, ovs_bridge->name, new_ports);
}
/* The bridge is still alive */
json_array_append_new(new_bridges, json_pack("[s,s]", "uuid", ovs_bridge->bridge_uuid));
}
}
if (bridges_changed) {
/* A port needs to be deleted from this bridge */
/* A bridge needs to be deleted */
_expect_ovs_bridges(params, priv->db_uuid, bridges);
_set_ovs_bridges(params, priv->db_uuid, new_bridges);
}

View file

@ -196,7 +196,8 @@ static void periodic_update(NMDeviceWifi *self);
static void ap_add_remove(NMDeviceWifi *self,
gboolean is_adding,
NMWifiAP *ap,
gboolean recheck_available_connections);
gboolean recheck_available_connections,
gboolean recheck_auto_activate);
static void _hw_addr_set_scanning(NMDeviceWifi *self, gboolean do_reset);
@ -714,7 +715,10 @@ update_seen_bssids_cache(NMDeviceWifi *self, NMWifiAP *ap)
}
static void
set_current_ap(NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available_connections)
set_current_ap(NMDeviceWifi *self,
NMWifiAP *new_ap,
gboolean recheck_available_connections,
gboolean recheck_auto_activate)
{
NMDeviceWifiPrivate *priv;
NMWifiAP *old_ap;
@ -741,7 +745,11 @@ set_current_ap(NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available_
/* Remove any AP from the internal list if it was created by NM or isn't known to the supplicant */
if (NM_IN_SET(mode, _NM_802_11_MODE_ADHOC, _NM_802_11_MODE_AP)
|| nm_wifi_ap_get_fake(old_ap))
ap_add_remove(self, FALSE, old_ap, recheck_available_connections);
ap_add_remove(self,
FALSE,
old_ap,
recheck_available_connections,
recheck_auto_activate);
g_object_unref(old_ap);
}
@ -814,7 +822,8 @@ static void
ap_add_remove(NMDeviceWifi *self,
gboolean is_adding, /* or else removing */
NMWifiAP *ap,
gboolean recheck_available_connections)
gboolean recheck_available_connections,
gboolean recheck_auto_activate)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
@ -845,13 +854,14 @@ ap_add_remove(NMDeviceWifi *self,
nm_dbus_object_clear_and_unexport(&ap);
}
if (recheck_auto_activate)
nm_device_recheck_auto_activate_schedule(NM_DEVICE(self));
if (recheck_available_connections)
nm_device_recheck_available_connections(NM_DEVICE(self));
}
static void
remove_all_aps(NMDeviceWifi *self)
remove_all_aps(NMDeviceWifi *self, gboolean disposing)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
NMWifiAP *ap;
@ -859,11 +869,12 @@ remove_all_aps(NMDeviceWifi *self)
if (c_list_is_empty(&priv->aps_lst_head))
return;
set_current_ap(self, NULL, FALSE);
set_current_ap(self, NULL, FALSE, !disposing);
while ((ap = c_list_first_entry(&priv->aps_lst_head, NMWifiAP, aps_lst)))
ap_add_remove(self, FALSE, ap, FALSE);
ap_add_remove(self, FALSE, ap, FALSE, !disposing);
if (!disposing)
nm_device_recheck_available_connections(NM_DEVICE(self));
}
@ -951,7 +962,7 @@ deactivate(NMDevice *device)
priv->rate = 0;
set_current_ap(self, NULL, TRUE);
set_current_ap(self, NULL, TRUE, TRUE);
if (!wake_on_wlan_restore(self))
_LOGW(LOGD_DEVICE | LOGD_WIFI, "Cannot unconfigure WoWLAN.");
@ -2000,7 +2011,7 @@ supplicant_iface_bss_changed_cb(NMSupplicantInterface *iface,
if (nm_wifi_ap_set_fake(found_ap, TRUE))
_ap_dump(self, LOGL_DEBUG, found_ap, "updated", 0);
} else {
ap_add_remove(self, FALSE, found_ap, TRUE);
ap_add_remove(self, FALSE, found_ap, TRUE, TRUE);
schedule_ap_list_dump(self);
}
return;
@ -2043,7 +2054,7 @@ supplicant_iface_bss_changed_cb(NMSupplicantInterface *iface,
}
}
ap_add_remove(self, TRUE, ap, TRUE);
ap_add_remove(self, TRUE, ap, TRUE, TRUE);
}
/* Update the current AP if the supplicant notified a current BSS change
@ -2268,7 +2279,7 @@ link_timeout_cb(gpointer user_data)
if (nm_device_get_state(device) != NM_DEVICE_STATE_ACTIVATED)
return FALSE;
set_current_ap(self, NULL, TRUE);
set_current_ap(self, NULL, TRUE, TRUE);
nm_device_state_changed(device,
NM_DEVICE_STATE_FAILED,
@ -2684,7 +2695,7 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface,
}
}
set_current_ap(self, new_ap, TRUE);
set_current_ap(self, new_ap, TRUE, TRUE);
req = nm_device_get_act_request(NM_DEVICE(self));
if (req) {
@ -2935,7 +2946,8 @@ build_supplicant_config(NMDeviceWifi *self,
s_wireless = nm_connection_get_setting_wireless(connection);
g_return_val_if_fail(s_wireless != NULL, NULL);
config = nm_supplicant_config_new(nm_supplicant_interface_get_capabilities(priv->sup_iface));
config = nm_supplicant_config_new(nm_supplicant_interface_get_capabilities(priv->sup_iface),
nm_utils_get_connection_first_permissions_user(connection));
/* Warn if AP mode may not be supported */
if (nm_streq0(nm_setting_wireless_get_mode(s_wireless), NM_SETTING_WIRELESS_MODE_AP)
@ -3011,6 +3023,7 @@ build_supplicant_config(NMDeviceWifi *self,
mtu,
pmf,
fils,
nm_device_get_private_files(NM_DEVICE(self)),
error)) {
g_prefix_error(error, "802-11-wireless-security: ");
goto error;
@ -3118,7 +3131,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
priv->mode = _NM_802_11_MODE_AP;
/* Scanning not done in AP mode; clear the scan list */
remove_all_aps(self);
remove_all_aps(self, FALSE);
} else if (g_strcmp0(mode, NM_SETTING_WIRELESS_MODE_MESH) == 0)
priv->mode = _NM_802_11_MODE_MESH;
_notify(self, PROP_MODE);
@ -3155,14 +3168,14 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
nm_wifi_ap_set_address(ap_fake, nm_device_get_hw_address(device));
g_object_freeze_notify(G_OBJECT(self));
ap_add_remove(self, TRUE, ap_fake, TRUE);
ap_add_remove(self, TRUE, ap_fake, TRUE, TRUE);
g_object_thaw_notify(G_OBJECT(self));
ap = ap_fake;
}
_scan_notify_allowed(self, NM_TERNARY_DEFAULT);
set_current_ap(self, ap, FALSE);
set_current_ap(self, ap, FALSE, TRUE);
nm_active_connection_set_specific_object(NM_ACTIVE_CONNECTION(req),
nm_dbus_object_get_path(NM_DBUS_OBJECT(ap)));
return NM_ACT_STAGE_RETURN_SUCCESS;
@ -3528,7 +3541,7 @@ device_state_changed(NMDevice *device,
cleanup_association_attempt(self, TRUE);
cleanup_supplicant_failures(self);
remove_all_aps(self);
remove_all_aps(self, FALSE);
}
switch (new_state) {
@ -3566,7 +3579,7 @@ device_state_changed(NMDevice *device,
}
if (clear_aps)
remove_all_aps(self);
remove_all_aps(self, FALSE);
_scan_notify_allowed(self, NM_TERNARY_DEFAULT);
}
@ -3808,7 +3821,7 @@ dispose(GObject *object)
g_clear_object(&priv->sup_mgr);
remove_all_aps(self);
remove_all_aps(self, TRUE);
if (priv->p2p_device) {
/* Destroy the P2P device. */

View file

@ -586,7 +586,11 @@ add_dns_domains(GPtrArray *array,
}
static void
merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3ConfigData *l3cd)
merge_one_l3cd(NMResolvConfData *rc,
int addr_family,
int ifindex,
const NML3ConfigData *l3cd,
gboolean ignore_searches_and_options)
{
char buf[NM_INET_ADDRSTRLEN + 50];
gboolean has_trust_ad;
@ -624,6 +628,7 @@ merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3Con
add_string_item(rc->nameservers, buf, TRUE);
}
if (!ignore_searches_and_options) {
add_dns_domains(rc->searches, addr_family, l3cd, FALSE, TRUE);
has_trust_ad = FALSE;
@ -648,6 +653,7 @@ merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3Con
rc->has_trust_ad = NM_TERNARY_TRUE;
} else
rc->has_trust_ad = NM_TERNARY_FALSE;
}
if (addr_family == AF_INET) {
const in_addr_t *nis_servers;
@ -1231,12 +1237,15 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[
{
nm_auto_free_checksum GChecksum *sum = NULL;
NMDnsConfigIPData *ip_data;
gboolean has_global_dns_section = FALSE;
sum = g_checksum_new(G_CHECKSUM_SHA1);
nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1));
if (global)
if (global) {
nm_global_dns_config_update_checksum(global, sum);
has_global_dns_section = nm_global_dns_has_global_dns_section(global);
}
if (!global || !nm_global_dns_config_lookup_domain(global, "*")) {
const CList *head;
@ -1248,7 +1257,8 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[
nm_l3_config_data_hash_dns(ip_data->l3cd,
sum,
ip_data->addr_family,
ip_data->ip_config_type);
ip_data->ip_config_type,
has_global_dns_section);
}
}
@ -1264,6 +1274,9 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
const char *const *servers;
guint i;
/* Global config must be processed before connections' config */
nm_assert(rc->nameservers->len == 0);
if (!global_conf)
return FALSE;
@ -1351,12 +1364,17 @@ _collect_resolv_conf_data(NMDnsManager *self,
.nis_servers = g_ptr_array_new(),
.has_trust_ad = NM_TERNARY_DEFAULT,
};
gboolean has_global_dns_section = FALSE;
priv = NM_DNS_MANAGER_GET_PRIVATE(self);
if (global_config)
if (global_config) {
merge_global_dns_config(&rc, global_config);
has_global_dns_section = nm_global_dns_has_global_dns_section(global_config);
}
/* If global nameservers are defined, no DNS configs are used from connections at all,
* including searches and options. */
if (!global_config || !nm_global_dns_config_lookup_domain(global_config, "*")) {
nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE);
int first_prio = 0;
@ -1390,8 +1408,16 @@ _collect_resolv_conf_data(NMDnsManager *self,
skip ? "<SKIP>" : "",
get_nameserver_list(ip_data->addr_family, ip_data->l3cd, &tmp_strbuf));
if (!skip)
merge_one_l3cd(&rc, ip_data->addr_family, ip_data->data->ifindex, ip_data->l3cd);
if (!skip) {
/* Merge the configs from connections. However, if there was a [global-dns]
* it overwrites searches and options from the connections, thus we only
* merge the nameservers. */
merge_one_l3cd(&rc,
ip_data->addr_family,
ip_data->data->ifindex,
ip_data->l3cd,
has_global_dns_section);
}
}
}

View file

@ -50,9 +50,9 @@ struct _NMGlobalDnsConfig {
char **options;
GHashTable *domains;
const char **domain_list;
gboolean internal;
char *cert_authority;
NMDnsResolveMode resolve_mode;
gboolean internal;
};
/*****************************************************************************/
@ -941,6 +941,14 @@ next:
/*****************************************************************************/
gboolean
nm_global_dns_has_global_dns_section(const NMGlobalDnsConfig *dns_config)
{
g_return_val_if_fail(dns_config, FALSE);
return dns_config->searches != NULL || dns_config->options != NULL;
}
const char *const *
nm_global_dns_config_get_searches(const NMGlobalDnsConfig *dns_config)
{
@ -1236,6 +1244,7 @@ load_global_dns(GKeyFile *keyfile, gboolean internal)
gs_free char *cert_authority = NULL;
gs_free char *resolve_mode = NULL;
NMDnsResolveMode parsed_resolve_mode;
gboolean has_global_dns_section;
if (internal) {
group = NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS;
@ -1386,6 +1395,22 @@ load_global_dns(GKeyFile *keyfile, gboolean internal)
return NULL;
}
/* Defining [global-dns-domain-*] implies defining [global-dns] too (maybe empty) */
if (default_found)
has_global_dns_section = TRUE;
else
has_global_dns_section = g_key_file_has_group(keyfile, group);
/* If there exist a [global-dns] section, always initialize "searches" and "options" so
* they appear in D-Bus. Clients can use this to know if it's defined, so they can know
* if DNS configs from connections are relevant or not. */
if (has_global_dns_section) {
if (!dns_config->searches)
dns_config->searches = nm_strv_empty_new();
if (!dns_config->options)
dns_config->options = nm_strv_empty_new();
}
dns_config->internal = internal;
global_dns_config_seal_domains(dns_config);
return dns_config;
@ -1606,17 +1631,6 @@ nm_global_dns_config_from_dbus(const GValue *value, GError **error)
g_variant_unref(val);
}
/* An empty value is valid and clears the internal configuration */
if (!nm_global_dns_config_is_empty(dns_config)
&& !nm_global_dns_config_lookup_domain(dns_config, "*")) {
g_set_error_literal(error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_FAILED,
"Global DNS configuration is missing the default domain");
nm_global_dns_config_free(dns_config);
return NULL;
}
global_dns_config_seal_domains(dns_config);
return dns_config;
}

View file

@ -274,6 +274,7 @@ gboolean nm_config_data_is_intern_atomic_group(const NMConfigData *self, const c
GKeyFile *nm_config_data_clone_keyfile_intern(const NMConfigData *self);
gboolean nm_global_dns_has_global_dns_section(const NMGlobalDnsConfig *dns_config);
const char *const *nm_global_dns_config_get_searches(const NMGlobalDnsConfig *dns_config);
const char *const *nm_global_dns_config_get_options(const NMGlobalDnsConfig *dns_config);
const char *nm_global_dns_config_get_certification_authority(const NMGlobalDnsConfig *dns_config);

View file

@ -18,6 +18,7 @@
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-core-intern/nm-keyfile-internal.h"
#include "libnm-core-intern/nm-keyfile-utils.h"
#include "libnm-glib-aux/nm-keyfile-aux.h"
#define DEFAULT_CONFIG_MAIN_FILE NMCONFDIR "/NetworkManager.conf"
#define DEFAULT_CONFIG_DIR NMCONFDIR "/conf.d"
@ -1046,6 +1047,10 @@ read_config(GKeyFile *keyfile,
/* internal groups cannot be set by user configuration. */
continue;
}
if (!g_key_file_has_group(keyfile, group))
nm_key_file_add_group(keyfile, group);
keys = g_key_file_get_keys(kf, group, &nkeys, NULL);
if (!keys)
continue;
@ -1639,6 +1644,12 @@ intern_config_read(const char *filename,
"");
}
if (!g_key_file_has_group(keyfile_intern, group)) {
nm_key_file_add_group(keyfile_intern, group);
if (is_intern)
has_intern = TRUE;
}
for (k = 0; keys[k]; k++) {
gs_free char *value_set = NULL;
const char *key = keys[k];
@ -1823,6 +1834,9 @@ intern_config_write(const char *filename,
}
}
if (!g_key_file_has_group(keyfile, group))
nm_key_file_add_group(keyfile, group);
for (k = 0; keys[k]; k++) {
const char *key = keys[k];
gs_free char *value_set = NULL;

View file

@ -5011,6 +5011,7 @@ typedef struct {
int child_stdin;
int child_stdout;
int child_stderr;
gboolean binary_output;
GSource *input_source;
GSource *output_source;
GSource *error_source;
@ -5090,9 +5091,17 @@ helper_complete(HelperInfo *info, GError *error)
}
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
if (info->binary_output) {
g_task_return_pointer(
info->task,
g_bytes_new(nm_str_buf_get_str_unsafe(&info->in_buffer), info->in_buffer.len),
(GDestroyNotify) (g_bytes_unref));
} else {
g_task_return_pointer(info->task,
nm_str_buf_finalize(&info->in_buffer, NULL) ?: g_new0(char, 1),
g_free);
}
helper_info_free(info);
}
@ -5235,6 +5244,7 @@ helper_cancelled(GObject *object, gpointer user_data)
void
nm_utils_spawn_helper(const char *const *args,
gboolean binary_output,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer cb_data)
@ -5251,8 +5261,13 @@ nm_utils_spawn_helper(const char *const *args,
info = g_new(HelperInfo, 1);
*info = (HelperInfo) {
.task = nm_g_task_new(NULL, cancellable, nm_utils_spawn_helper, callback, cb_data),
.binary_output = binary_output,
};
/* Store if the caller requested binary output so that we can check later
* that the right result function is called. */
g_task_set_task_data(info->task, GINT_TO_POINTER(binary_output), NULL);
if (!g_spawn_async_with_pipes("/",
(char **) NM_MAKE_STRV(LIBEXECDIR "/nm-daemon-helper"),
(char **) NM_MAKE_STRV(),
@ -5363,11 +5378,25 @@ nm_utils_spawn_helper(const char *const *args,
}
char *
nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error)
nm_utils_spawn_helper_finish_string(GAsyncResult *result, GError **error)
{
GTask *task = G_TASK(result);
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_spawn_helper));
/* Check binary_output */
nm_assert(GPOINTER_TO_INT(g_task_get_task_data(task)) == FALSE);
return g_task_propagate_pointer(task, error);
}
GBytes *
nm_utils_spawn_helper_finish_binary(GAsyncResult *result, GError **error)
{
GTask *task = G_TASK(result);
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_spawn_helper));
/* Check binary_output */
nm_assert(GPOINTER_TO_INT(g_task_get_task_data(task)) == TRUE);
return g_task_propagate_pointer(task, error);
}
@ -5474,3 +5503,185 @@ nm_utils_shorten_hostname(const char *hostname, char **shortened)
*shortened = g_steal_pointer(&s);
return TRUE;
}
const char *
nm_utils_get_connection_first_permissions_user(NMConnection *connection)
{
NMSettingConnection *s_con;
s_con = nm_connection_get_setting_connection(connection);
nm_assert(s_con);
return _nm_setting_connection_get_first_permissions_user(s_con);
}
/*****************************************************************************/
const char **
nm_utils_get_connection_private_files_paths(NMConnection *connection)
{
GPtrArray *files;
gs_free NMSetting **settings = NULL;
guint num_settings;
guint i;
files = g_ptr_array_new();
settings = nm_connection_get_settings(connection, &num_settings);
for (i = 0; i < num_settings; i++) {
_nm_setting_get_private_files(settings[i], files);
}
g_ptr_array_add(files, NULL);
return (const char **) g_ptr_array_free(files, files->len == 1);
}
typedef struct _ReadInfo ReadInfo;
typedef struct {
char *path;
ReadInfo *read_info;
} FileInfo;
struct _ReadInfo {
GTask *task;
GHashTable *table;
GPtrArray *file_infos; /* of FileInfo */
GError *first_error;
guint num_pending;
};
static void
read_file_helper_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
FileInfo *file_info = user_data;
ReadInfo *read_info = file_info->read_info;
gs_unref_bytes GBytes *output = NULL;
gs_free_error GError *error = NULL;
output = nm_utils_spawn_helper_finish_binary(result, &error);
nm_assert(read_info->num_pending > 0);
read_info->num_pending--;
if (nm_utils_error_is_cancelled(error)) {
/* nop */
} else if (error) {
nm_log_dbg(LOGD_CORE,
"read-private-files: failed to read file '%s': %s",
file_info->path,
error->message);
if (!read_info->first_error) {
/* @error just says "helper process exited with status X".
* Return a more human-friendly one. */
read_info->first_error = g_error_new(NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"error reading file '%s'",
file_info->path);
}
} else {
nm_log_dbg(LOGD_SUPPLICANT,
"read-private-files: successfully read file '%s'",
file_info->path);
/* Store the file contents in the hash table */
if (!read_info->table) {
read_info->table = g_hash_table_new_full(nm_str_hash,
g_str_equal,
g_free,
(GDestroyNotify) g_bytes_unref);
}
g_hash_table_insert(read_info->table,
g_steal_pointer(&file_info->path),
g_steal_pointer(&output));
}
g_clear_pointer(&file_info->path, g_free);
/* If all operations are completed, return */
if (read_info->num_pending == 0) {
if (read_info->first_error) {
g_task_return_error(read_info->task, g_steal_pointer(&read_info->first_error));
} else {
g_task_return_pointer(read_info->task,
g_steal_pointer(&read_info->table),
(GDestroyNotify) g_hash_table_unref);
}
if (read_info->table)
g_hash_table_unref(read_info->table);
if (read_info->file_infos)
g_ptr_array_unref(read_info->file_infos);
g_object_unref(read_info->task);
g_free(read_info);
}
}
/**
* nm_utils_read_private_files:
* @paths: array of file paths to be read
* @user: name of the user to impersonate when reading the files
* @cancellable: cancellable to cancel the operation
* @callback: callback to invoke on completion
* @cb_data: data for @callback
*
* Reads the given list of files @paths on behalf of user @user. Invokes
* @callback asynchronously on completion. The callback must use
* nm_utils_read_private_files_finish() to obtain the result.
*/
void
nm_utils_read_private_files(const char *const *paths,
const char *user,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer cb_data)
{
ReadInfo *read_info;
FileInfo *file_info;
guint i;
g_return_if_fail(paths && paths[0]);
g_return_if_fail(cancellable);
g_return_if_fail(callback);
g_return_if_fail(cb_data);
read_info = g_new(ReadInfo, 1);
*read_info = (ReadInfo) {
.task = nm_g_task_new(NULL, cancellable, nm_utils_read_private_files, callback, cb_data),
.file_infos = g_ptr_array_new_with_free_func(g_free),
};
for (i = 0; paths[i]; i++) {
file_info = g_new(FileInfo, 1);
*file_info = (FileInfo) {
.path = g_strdup(paths[i]),
.read_info = read_info,
};
g_ptr_array_add(read_info->file_infos, file_info);
read_info->num_pending++;
nm_utils_spawn_helper(NM_MAKE_STRV("read-file-as-user", user, paths[i]),
TRUE,
cancellable,
read_file_helper_cb,
file_info);
}
}
/**
* nm_utils_read_private_files_finish:
* @result: the GAsyncResult
* @error: on return, the error
*
* Returns the files read by nm_utils_read_private_files(). The return value
* is a hash table {char * -> GBytes *}. Free it with g_hash_table_unref().
*/
GHashTable *
nm_utils_read_private_files_finish(GAsyncResult *result, GError **error)
{
GTask *task = G_TASK(result);
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_read_private_files));
return g_task_propagate_pointer(task, error);
}

View file

@ -478,11 +478,13 @@ guint8 nm_wifi_utils_level_to_quality(int val);
/*****************************************************************************/
void nm_utils_spawn_helper(const char *const *args,
gboolean binary_output,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer cb_data);
char *nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error);
char *nm_utils_spawn_helper_finish_string(GAsyncResult *result, GError **error);
GBytes *nm_utils_spawn_helper_finish_binary(GAsyncResult *result, GError **error);
/*****************************************************************************/
@ -490,4 +492,19 @@ uid_t nm_utils_get_nm_uid(void);
gid_t nm_utils_get_nm_gid(void);
/*****************************************************************************/
const char *nm_utils_get_connection_first_permissions_user(NMConnection *connection);
/*****************************************************************************/
const char **nm_utils_get_connection_private_files_paths(NMConnection *connection);
void nm_utils_read_private_files(const char *const *paths,
const char *user,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer cb_data);
GHashTable *nm_utils_read_private_files_finish(GAsyncResult *result, GError **error);
#endif /* __NM_CORE_UTILS_H__ */

View file

@ -3139,7 +3139,8 @@ void
nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd,
GChecksum *sum,
int addr_family,
NMDnsIPConfigType dns_ip_config_type)
NMDnsIPConfigType dns_ip_config_type,
gboolean ignore_searches_and_options)
{
guint i;
int val;
@ -3178,6 +3179,7 @@ nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd,
empty = FALSE;
}
if (!ignore_searches_and_options) {
searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches);
for (i = 0; i < num_searches; i++) {
g_checksum_update(sum, (const guint8 *) searches[i], strlen(searches[i]));
@ -3189,6 +3191,7 @@ nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd,
g_checksum_update(sum, (const guint8 *) options[i], strlen(options[i]));
empty = FALSE;
}
}
val = nm_l3_config_data_get_mdns(l3cd);
if (val != NM_SETTING_CONNECTION_MDNS_DEFAULT) {

View file

@ -615,6 +615,7 @@ nmtst_l3_config_data_get_best_gateway(const NML3ConfigData *self, int addr_famil
void nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd,
GChecksum *sum,
int addr_family,
NMDnsIPConfigType dns_ip_config_type);
NMDnsIPConfigType dns_ip_config_type,
gboolean ignore_searches_and_options);
#endif /* __NM_L3_CONFIG_DATA_H__ */

View file

@ -136,6 +136,7 @@ enum {
ACTIVE_CONNECTION_REMOVED,
CONFIGURE_QUIT,
DEVICE_IFINDEX_CHANGED,
SHARING_IPV4_CHANGED,
LAST_SIGNAL
};
@ -238,6 +239,8 @@ typedef struct {
guint8 device_state_prune_ratelimit_count;
guint shared_connections_ip4_count;
bool startup : 1;
bool devices_inited : 1;
@ -1960,7 +1963,7 @@ find_device_by_iface(NMManager *self,
}
static gboolean
manager_sleeping(NMManager *self)
manager_is_disabled(NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
@ -1973,8 +1976,8 @@ static const char *
_nm_state_to_string(NMState state)
{
switch (state) {
case NM_STATE_ASLEEP:
return "ASLEEP";
case NM_STATE_DISABLED:
return "DISABLED";
case NM_STATE_DISCONNECTED:
return "DISCONNECTED";
case NM_STATE_DISCONNECTING:
@ -2078,15 +2081,18 @@ nm_manager_update_state(NMManager *self)
{
NMManagerPrivate *priv;
NMState new_state = NM_STATE_DISCONNECTED;
const char *detail = "";
g_return_if_fail(NM_IS_MANAGER(self));
priv = NM_MANAGER_GET_PRIVATE(self);
if (manager_sleeping(self))
new_state = NM_STATE_ASLEEP;
else
if (manager_is_disabled(self)) {
new_state = NM_STATE_DISABLED;
detail = priv->sleeping ? " (ASLEEP)" : " (NETWORKING OFF)";
} else {
new_state = find_best_device_state(self);
}
if (new_state >= NM_STATE_CONNECTED_LOCAL && priv->connectivity_state == NM_CONNECTIVITY_FULL) {
new_state = NM_STATE_CONNECTED_GLOBAL;
@ -2097,7 +2103,7 @@ nm_manager_update_state(NMManager *self)
priv->state = new_state;
_LOGI(LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string(new_state));
_LOGI(LOGD_CORE, "NetworkManager state is now %s%s", _nm_state_to_string(new_state), detail);
_notify(self, PROP_STATE);
nm_dbus_object_emit_signal(NM_DBUS_OBJECT(self),
@ -2956,7 +2962,7 @@ _rfkill_update_devices(NMManager *self, NMRfkillType rtype, gboolean enabled)
_notify(self, _rfkill_type_desc[rtype].prop_id);
/* Don't touch devices if asleep/networking disabled */
if (manager_sleeping(self))
if (manager_is_disabled(self))
return;
/* enable/disable wireless devices as required */
@ -3120,7 +3126,7 @@ _rfkill_update_from_user(NMManager *self, NMRfkillType rtype, gboolean enabled)
gboolean old_enabled, new_enabled;
/* Don't touch devices if asleep/networking disabled */
if (manager_sleeping(self))
if (manager_is_disabled(self))
return;
_LOGD(LOGD_RFKILL,
@ -4079,7 +4085,7 @@ add_device(NMManager *self, NMDevice *device, GError **error)
nm_device_set_unmanaged_by_user_settings(device, TRUE);
nm_device_set_unmanaged_flags(device, NM_UNMANAGED_SLEEPING, manager_sleeping(self));
nm_device_set_unmanaged_flags(device, NM_UNMANAGED_MANAGER_DISABLED, manager_is_disabled(self));
dbus_path = nm_dbus_object_export(NM_DBUS_OBJECT(device));
_LOG2I(LOGD_DEVICE, device, "new %s device (%s)", type_desc, dbus_path);
@ -5718,6 +5724,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
GError *local = NULL;
NMConnectionMultiConnect multi_connect;
const char *parent_spec;
gboolean did_realize = FALSE;
g_return_val_if_fail(NM_IS_MANAGER(self), FALSE);
g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(active), FALSE);
@ -5892,6 +5899,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
nm_device_get_iface(device));
return FALSE;
}
did_realize = TRUE;
}
}
@ -5923,7 +5931,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
"The controller connection '%s' is not compatible with '%s'",
nm_settings_connection_get_id(controller_connection),
nm_settings_connection_get_id(sett_conn));
return FALSE;
goto err_unrealize;
}
if (!controller_ac) {
@ -5946,7 +5954,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
"Controller connection '%s' can't be activated: ",
nm_settings_connection_get_id(controller_connection));
}
return FALSE;
goto err_unrealize;
}
}
@ -6040,7 +6048,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_DEPENDENCY_FAILED,
"Activation failed because the device is unmanaged");
return FALSE;
goto err_unrealize;
}
}
@ -6048,6 +6056,11 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
active_connection_add(self, active);
nm_device_queue_activation(device, NM_ACT_REQUEST(active));
return TRUE;
err_unrealize:
if (did_realize)
nm_device_unrealize(device, TRUE, NULL);
return FALSE;
}
static gboolean
@ -7292,7 +7305,7 @@ device_sleep_cb(NMDevice *device, GParamSpec *pspec, NMManager *self)
case NM_DEVICE_STATE_DISCONNECTED:
_LOGD(LOGD_SUSPEND, "sleep: unmanaging device %s", nm_device_get_ip_iface(device));
nm_device_set_unmanaged_by_flags_queue(device,
NM_UNMANAGED_SLEEPING,
NM_UNMANAGED_MANAGER_DISABLED,
NM_UNMAN_FLAG_OP_SET_UNMANAGED,
NM_DEVICE_STATE_REASON_SLEEPING);
break;
@ -7314,24 +7327,26 @@ _handle_device_takedown(NMManager *self,
gboolean suspending,
gboolean is_shutdown)
{
gboolean is_sleep = suspending || is_shutdown;
NMDeviceStateReason reason =
is_sleep ? NM_DEVICE_STATE_REASON_SLEEPING : NM_DEVICE_STATE_REASON_NETWORKING_OFF;
nm_device_notify_sleeping(device);
if (nm_device_is_activating(device)
|| nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) {
_LOGD(LOGD_SUSPEND,
_LOGD(is_sleep ? LOGD_SUSPEND : LOGD_CORE,
"%s: wait disconnection of device %s",
is_shutdown ? "shutdown" : "sleep",
is_sleep ? (is_shutdown ? "shutdown" : "sleep") : "networking off",
nm_device_get_ip_iface(device));
if (sleep_devices_add(self, device, suspending))
nm_device_queue_state(device,
NM_DEVICE_STATE_DEACTIVATING,
NM_DEVICE_STATE_REASON_SLEEPING);
nm_device_queue_state(device, NM_DEVICE_STATE_DEACTIVATING, reason);
} else {
nm_device_set_unmanaged_by_flags(device,
NM_UNMANAGED_SLEEPING,
NM_UNMANAGED_MANAGER_DISABLED,
NM_UNMAN_FLAG_OP_SET_UNMANAGED,
NM_DEVICE_STATE_REASON_SLEEPING);
reason);
}
}
@ -7345,8 +7360,10 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
suspending = sleeping_changed && priv->sleeping;
waking_from_suspend = sleeping_changed && !priv->sleeping;
if (manager_sleeping(self)) {
_LOGD(LOGD_SUSPEND, "sleep: %s...", suspending ? "sleeping" : "disabling");
if (manager_is_disabled(self)) {
_LOGD(suspending ? LOGD_SUSPEND : LOGD_CORE,
"%s...",
suspending ? "sleep: sleeping" : "networking: disabling");
/* FIXME: are there still hardware devices that need to be disabled around
* suspend/resume?
@ -7372,7 +7389,9 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
_handle_device_takedown(self, device, suspending, FALSE);
}
} else {
_LOGD(LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling");
_LOGD(waking_from_suspend ? LOGD_SUSPEND : LOGD_CORE,
"%s...",
waking_from_suspend ? "sleep: waking up" : "networking: re-enabling");
sleep_devices_clear(self);
@ -7386,7 +7405,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
*/
if (device_is_wake_on_lan(priv->platform, device))
nm_device_set_unmanaged_by_flags(device,
NM_UNMANAGED_SLEEPING,
NM_UNMANAGED_MANAGER_DISABLED,
NM_UNMAN_FLAG_OP_SET_UNMANAGED,
NM_DEVICE_STATE_REASON_SLEEPING);
@ -7414,10 +7433,12 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
guint i;
if (nm_device_is_software(device)
&& !nm_device_get_unmanaged_flags(device, NM_UNMANAGED_SLEEPING)) {
&& !nm_device_get_unmanaged_flags(device, NM_UNMANAGED_MANAGER_DISABLED)) {
/* DHCP leases of software devices could have gone stale
* so we need to renew them. */
nm_device_update_dynamic_ip_setup(device, "wake up");
nm_device_update_dynamic_ip_setup(device,
waking_from_suspend ? "wake up"
: "networking on");
continue;
}
@ -7448,7 +7469,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED
: NM_DEVICE_STATE_REASON_NOW_MANAGED;
nm_device_set_unmanaged_by_flags(device,
NM_UNMANAGED_SLEEPING,
NM_UNMANAGED_MANAGER_DISABLED,
NM_UNMAN_FLAG_OP_SET_MANAGED,
reason);
}
@ -8811,6 +8832,41 @@ nm_manager_emit_device_ifindex_changed(NMManager *self, NMDevice *device)
g_signal_emit(self, signals[DEVICE_IFINDEX_CHANGED], 0, device);
}
void
nm_manager_update_shared_connection(NMManager *self, int addr_family, gboolean enabled)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
gboolean state_changed, state;
/* Only IPv4 supported for the moment */
if (addr_family != AF_INET)
return;
if (enabled) {
g_return_if_fail(priv->shared_connections_ip4_count < G_MAXUINT);
priv->shared_connections_ip4_count++;
state_changed = priv->shared_connections_ip4_count == 1;
} else {
g_return_if_fail(priv->shared_connections_ip4_count > 0);
priv->shared_connections_ip4_count--;
state_changed = priv->shared_connections_ip4_count == 0;
}
if (state_changed) {
state = priv->shared_connections_ip4_count > 0;
_LOGD(LOGD_SHARING, "sharing-ipv4 state change %d -> %d", !state, state);
g_signal_emit(self, signals[SHARING_IPV4_CHANGED], 0, state);
}
}
gboolean
nm_manager_get_sharing_ipv4(NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
return priv->shared_connections_ip4_count > 0;
}
/*****************************************************************************/
NM_DEFINE_SINGLETON_REGISTER(NMManager);
@ -9914,6 +9970,17 @@ nm_manager_class_init(NMManagerClass *manager_class)
G_TYPE_NONE,
1,
NM_TYPE_DEVICE);
signals[SHARING_IPV4_CHANGED] = g_signal_new(NM_MANAGER_SHARING_IPV4_CHANGED,
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_BOOLEAN);
}
NMConfig *

View file

@ -61,6 +61,7 @@
#define NM_MANAGER_CONFIGURE_QUIT "configure-quit"
#define NM_MANAGER_INTERNAL_DEVICE_ADDED "internal-device-added"
#define NM_MANAGER_INTERNAL_DEVICE_REMOVED "internal-device-removed"
#define NM_MANAGER_SHARING_IPV4_CHANGED "sharing-ipv4-changed"
GType nm_manager_get_type(void);
@ -212,6 +213,9 @@ struct _NMDnsManager;
struct _NMDnsManager *nm_manager_get_dns_manager(NMManager *self);
void nm_manager_update_shared_connection(NMManager *self, int addr_family, gboolean enabled);
gboolean nm_manager_get_sharing_ipv4(NMManager *self);
/*****************************************************************************/
void nm_manager_notify_delete_settings_connections(NMManager *self,

View file

@ -18,7 +18,6 @@
#include "NetworkManagerUtils.h"
#include "devices/nm-device.h"
#include "devices/nm-device-factory.h"
#include "devices/nm-device-private.h"
#include "dns/nm-dns-manager.h"
#include "nm-act-request.h"
#include "nm-auth-utils.h"
@ -98,6 +97,7 @@ typedef struct {
bool updating_dns : 1;
GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */
} NMPolicyPrivate;
struct _NMPolicy {
@ -1845,7 +1845,7 @@ nm_policy_device_recheck_auto_activate_schedule(NMPolicy *self, NMDevice *device
priv = NM_POLICY_GET_PRIVATE(self);
if (nm_manager_get_state(priv->manager) == NM_STATE_ASLEEP)
if (nm_manager_get_state(priv->manager) == NM_STATE_DISABLED)
return;
if (!nm_device_autoconnect_allowed(device))
@ -2083,65 +2083,6 @@ unblock_autoconnect_for_ports_for_sett_conn(NMPolicy *self, NMSettingsConnection
unblock_autoconnect_for_ports(self, controller_device, controller_uuid_settings, NULL);
}
static void
refresh_forwarding(NMPolicy *self, NMDevice *device, gboolean is_activated_shared_device)
{
NMActiveConnection *ac;
NMDevice *tmp_device;
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
const CList *tmp_lst;
gboolean any_shared_active = false;
gint32 default_forwarding_v4;
const char *new_value = NULL;
/* FIXME: This implementation is still inefficient because refresh_forwarding()
* is called every time a device goes up or down, requiring a full scan of all
* active connections to determine if any shared connection is active. */
nm_manager_for_each_active_connection (priv->manager, ac, tmp_lst) {
NMSettingIPConfig *s_ip;
NMDevice *to_device = nm_active_connection_get_device(ac);
if (to_device) {
s_ip = nm_device_get_applied_setting(to_device, NM_TYPE_SETTING_IP4_CONFIG);
if (s_ip) {
if (nm_streq0(nm_device_get_effective_ip_config_method(to_device, AF_INET),
NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
any_shared_active = true;
break;
}
}
}
}
default_forwarding_v4 = nm_platform_sysctl_get_int32(
NM_PLATFORM_GET,
NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"),
0);
new_value = any_shared_active ? "1" : (default_forwarding_v4 ? "1" : "0");
nm_manager_for_each_device (priv->manager, tmp_device, tmp_lst) {
NMDeviceState state;
NMSettingIPConfigForwarding ipv4_forwarding;
state = nm_device_get_state(tmp_device);
if (state != NM_DEVICE_STATE_ACTIVATED)
continue;
ipv4_forwarding = nm_device_get_ipv4_forwarding(tmp_device);
if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_AUTO
|| (device == tmp_device && is_activated_shared_device)) {
gs_free char *sysctl_value = NULL;
sysctl_value = nm_device_sysctl_ip_conf_get(tmp_device, AF_INET, "forwarding");
if (!nm_streq0(sysctl_value, new_value))
nm_device_sysctl_ip_conf_set(tmp_device, AF_INET, "forwarding", new_value);
}
}
}
static void
activate_port_or_children_connections(NMPolicy *self,
NMDevice *device,
@ -2288,7 +2229,6 @@ device_state_changed(NMDevice *device,
NMActiveConnection *ac;
NMSettingsConnection *sett_conn = nm_device_get_settings_connection(device);
NMSettingConnection *s_con = NULL;
gboolean is_activated_shared_device = FALSE;
switch (nm_device_state_reason_check(reason)) {
case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
@ -2404,10 +2344,6 @@ device_state_changed(NMDevice *device,
}
}
}
if (!nm_device_get_refresh_forwarding_done(device)) {
refresh_forwarding(self, device, FALSE);
nm_device_set_refresh_forwarding_done(device, TRUE);
}
break;
case NM_DEVICE_STATE_ACTIVATED:
if (nm_device_get_device_type(device) == NM_DEVICE_TYPE_OVS_INTERFACE) {
@ -2440,20 +2376,11 @@ device_state_changed(NMDevice *device,
update_system_hostname(self, "routing and dns", TRUE);
nm_dns_manager_end_updates(priv->dns_manager, __func__);
is_activated_shared_device =
nm_streq0(nm_device_get_effective_ip_config_method(device, AF_INET),
NM_SETTING_IP4_CONFIG_METHOD_SHARED);
refresh_forwarding(self, device, is_activated_shared_device);
nm_device_set_refresh_forwarding_done(device, FALSE);
break;
case NM_DEVICE_STATE_UNMANAGED:
case NM_DEVICE_STATE_UNAVAILABLE:
if (old_state > NM_DEVICE_STATE_DISCONNECTED)
update_routing_and_dns(self, FALSE, device);
if (!nm_device_get_refresh_forwarding_done(device)) {
refresh_forwarding(self, device, FALSE);
nm_device_set_refresh_forwarding_done(device, TRUE);
}
break;
case NM_DEVICE_STATE_DEACTIVATING:
if (sett_conn) {
@ -2489,10 +2416,6 @@ device_state_changed(NMDevice *device,
}
}
ip6_remove_device_prefix_delegations(self, device);
if (!nm_device_get_refresh_forwarding_done(device)) {
refresh_forwarding(self, device, FALSE);
nm_device_set_refresh_forwarding_done(device, TRUE);
}
break;
case NM_DEVICE_STATE_DISCONNECTED:
g_signal_handlers_disconnect_by_func(device, device_dns_lookup_done, self);
@ -2509,10 +2432,6 @@ device_state_changed(NMDevice *device,
/* Device is now available for auto-activation */
nm_policy_device_recheck_auto_activate_schedule(self, device);
if (!nm_device_get_refresh_forwarding_done(device)) {
refresh_forwarding(self, device, FALSE);
nm_device_set_refresh_forwarding_done(device, TRUE);
}
break;
case NM_DEVICE_STATE_PREPARE:
@ -2528,10 +2447,6 @@ device_state_changed(NMDevice *device,
g_object_weak_unref(G_OBJECT(ac), pending_ac_gone, self);
g_object_unref(self);
}
if (!nm_device_get_refresh_forwarding_done(device)) {
refresh_forwarding(self, device, FALSE);
nm_device_set_refresh_forwarding_done(device, TRUE);
}
break;
case NM_DEVICE_STATE_IP_CONFIG:
/* We must have secrets if we got here. */
@ -2542,10 +2457,6 @@ device_state_changed(NMDevice *device,
sett_conn,
NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
FALSE);
if (!nm_device_get_refresh_forwarding_done(device)) {
refresh_forwarding(self, device, FALSE);
nm_device_set_refresh_forwarding_done(device, TRUE);
}
break;
case NM_DEVICE_STATE_SECONDARIES:
if (sett_conn)

View file

@ -30,6 +30,7 @@ typedef struct {
typedef struct {
GHashTable *config;
GHashTable *blobs;
char *private_user;
NMSupplCapMask capabilities;
guint32 ap_scan;
bool fast_required : 1;
@ -60,7 +61,7 @@ _get_capability(NMSupplicantConfigPrivate *priv, NMSupplCapType type)
}
NMSupplicantConfig *
nm_supplicant_config_new(NMSupplCapMask capabilities)
nm_supplicant_config_new(NMSupplCapMask capabilities, const char *private_user)
{
NMSupplicantConfigPrivate *priv;
NMSupplicantConfig *self;
@ -69,6 +70,7 @@ nm_supplicant_config_new(NMSupplCapMask capabilities)
priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self);
priv->capabilities = capabilities;
priv->private_user = g_strdup(private_user);
return self;
}
@ -258,19 +260,19 @@ static gboolean
nm_supplicant_config_add_blob_for_connection(NMSupplicantConfig *self,
GBytes *field,
const char *name,
const char *con_uid,
const char *con_uuid,
GError **error)
{
if (field && g_bytes_get_size(field)) {
gs_free char *uid = NULL;
gs_free char *blob_id = NULL;
char *p;
uid = g_strdup_printf("%s-%s", con_uid, name);
for (p = uid; *p; p++) {
blob_id = g_strdup_printf("%s-%s", con_uuid, name);
for (p = blob_id; *p; p++) {
if (*p == '/')
*p = '-';
}
if (!nm_supplicant_config_add_blob(self, name, field, uid, error))
if (!nm_supplicant_config_add_blob(self, name, field, blob_id, error))
return FALSE;
}
return TRUE;
@ -283,6 +285,7 @@ nm_supplicant_config_finalize(GObject *object)
g_hash_table_destroy(priv->config);
nm_clear_pointer(&priv->blobs, g_hash_table_destroy);
nm_clear_pointer(&priv->private_user, g_free);
G_OBJECT_CLASS(nm_supplicant_config_parent_class)->finalize(object);
}
@ -930,6 +933,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
guint32 mtu,
NMSettingWirelessSecurityPmf pmf,
NMSettingWirelessSecurityFils fils,
GHashTable *files,
GError **error)
{
NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self);
@ -1284,6 +1288,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
con_uuid,
mtu,
FALSE,
files,
error))
return FALSE;
}
@ -1365,6 +1370,7 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
const char *con_uuid,
guint32 mtu,
gboolean wired,
GHashTable *files,
GError **error)
{
NMSupplicantConfigPrivate *priv;
@ -1594,24 +1600,21 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
}
/* CA certificate */
path = NULL;
bytes = NULL;
if (ca_cert_override) {
if (!add_string_val(self, ca_cert_override, "ca_cert", FALSE, NULL, error))
return FALSE;
/* This is a build-time-configured system-wide file path, no need to pass
* it as a blob */
path = ca_cert_override;
} else {
switch (nm_setting_802_1x_get_ca_cert_scheme(setting)) {
case NM_SETTING_802_1X_CK_SCHEME_BLOB:
bytes = nm_setting_802_1x_get_ca_cert_blob(setting);
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"ca_cert",
con_uuid,
error))
return FALSE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PATH:
path = nm_setting_802_1x_get_ca_cert_path(setting);
if (!add_string_val(self, path, "ca_cert", FALSE, NULL, error))
return FALSE;
if (priv->private_user)
bytes = nm_g_hash_table_lookup(files, path);
break;
case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
if (!add_pkcs11_uri_with_pin(self,
@ -1627,26 +1630,32 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
break;
}
}
if (bytes) {
if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "ca_cert", con_uuid, error))
return FALSE;
} else if (path) {
/* Private connections cannot use paths other than the system CA store */
g_return_val_if_fail(ca_cert_override || !priv->private_user, FALSE);
if (!add_string_val(self, path, "ca_cert", FALSE, NULL, error))
return FALSE;
}
/* Phase 2 CA certificate */
path = NULL;
bytes = NULL;
if (ca_cert_override) {
if (!add_string_val(self, ca_cert_override, "ca_cert2", FALSE, NULL, error))
return FALSE;
/* This is a build-time-configured system-wide file path, no need to pass
* it as a blob */
path = ca_cert_override;
} else {
switch (nm_setting_802_1x_get_phase2_ca_cert_scheme(setting)) {
case NM_SETTING_802_1X_CK_SCHEME_BLOB:
bytes = nm_setting_802_1x_get_phase2_ca_cert_blob(setting);
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"ca_cert2",
con_uuid,
error))
return FALSE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PATH:
path = nm_setting_802_1x_get_phase2_ca_cert_path(setting);
if (!add_string_val(self, path, "ca_cert2", FALSE, NULL, error))
return FALSE;
if (priv->private_user)
bytes = nm_g_hash_table_lookup(files, path);
break;
case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
if (!add_pkcs11_uri_with_pin(
@ -1663,6 +1672,15 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
break;
}
}
if (bytes) {
if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "ca_cert2", con_uuid, error))
return FALSE;
} else if (path) {
/* Private connections cannot use paths other than the system CA store */
g_return_val_if_fail(ca_cert_override || !priv->private_user, FALSE);
if (!add_string_val(self, path, "ca_cert2", FALSE, NULL, error))
return FALSE;
}
/* Subject match */
value = nm_setting_802_1x_get_subject_match(setting);
@ -1714,21 +1732,17 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
/* Private key */
added = FALSE;
path = NULL;
bytes = NULL;
switch (nm_setting_802_1x_get_private_key_scheme(setting)) {
case NM_SETTING_802_1X_CK_SCHEME_BLOB:
bytes = nm_setting_802_1x_get_private_key_blob(setting);
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"private_key",
con_uuid,
error))
return FALSE;
added = TRUE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PATH:
path = nm_setting_802_1x_get_private_key_path(setting);
if (!add_string_val(self, path, "private_key", FALSE, NULL, error))
return FALSE;
if (priv->private_user)
bytes = nm_g_hash_table_lookup(files, path);
added = TRUE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
@ -1745,6 +1759,19 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
default:
break;
}
if (bytes) {
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"private_key",
con_uuid,
error))
return FALSE;
} else if (path) {
/* Private connections cannot use paths */
g_return_val_if_fail(!priv->private_user, FALSE);
if (!add_string_val(self, path, "private_key", FALSE, NULL, error))
return FALSE;
}
if (added) {
NMSetting8021xCKFormat format;
@ -1768,20 +1795,16 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
/* Only add the client cert if the private key is not PKCS#12, as
* wpa_supplicant configuration directs us to do.
*/
path = NULL;
bytes = NULL;
switch (nm_setting_802_1x_get_client_cert_scheme(setting)) {
case NM_SETTING_802_1X_CK_SCHEME_BLOB:
bytes = nm_setting_802_1x_get_client_cert_blob(setting);
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"client_cert",
con_uuid,
error))
return FALSE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PATH:
path = nm_setting_802_1x_get_client_cert_path(setting);
if (!add_string_val(self, path, "client_cert", FALSE, NULL, error))
return FALSE;
if (priv->private_user)
bytes = nm_g_hash_table_lookup(files, path);
break;
case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
if (!add_pkcs11_uri_with_pin(
@ -1797,26 +1820,35 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
default:
break;
}
if (bytes) {
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"client_cert",
con_uuid,
error))
return FALSE;
} else if (path) {
/* Private connections cannot use paths */
g_return_val_if_fail(!priv->private_user, FALSE);
if (!add_string_val(self, path, "client_cert", FALSE, NULL, error))
return FALSE;
}
}
}
/* Phase 2 private key */
added = FALSE;
path = NULL;
bytes = NULL;
switch (nm_setting_802_1x_get_phase2_private_key_scheme(setting)) {
case NM_SETTING_802_1X_CK_SCHEME_BLOB:
bytes = nm_setting_802_1x_get_phase2_private_key_blob(setting);
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"private_key2",
con_uuid,
error))
return FALSE;
added = TRUE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PATH:
path = nm_setting_802_1x_get_phase2_private_key_path(setting);
if (!add_string_val(self, path, "private_key2", FALSE, NULL, error))
return FALSE;
if (priv->private_user)
bytes = nm_g_hash_table_lookup(files, path);
added = TRUE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
@ -1834,6 +1866,19 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
default:
break;
}
if (bytes) {
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"private_key2",
con_uuid,
error))
return FALSE;
} else if (path) {
/* Private connections cannot use paths */
g_return_val_if_fail(!priv->private_user, FALSE);
if (!add_string_val(self, path, "private_key2", FALSE, NULL, error))
return FALSE;
}
if (added) {
NMSetting8021xCKFormat format;
@ -1857,20 +1902,16 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
/* Only add the client cert if the private key is not PKCS#12, as
* wpa_supplicant configuration directs us to do.
*/
path = NULL;
bytes = NULL;
switch (nm_setting_802_1x_get_phase2_client_cert_scheme(setting)) {
case NM_SETTING_802_1X_CK_SCHEME_BLOB:
bytes = nm_setting_802_1x_get_phase2_client_cert_blob(setting);
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"client_cert2",
con_uuid,
error))
return FALSE;
break;
case NM_SETTING_802_1X_CK_SCHEME_PATH:
path = nm_setting_802_1x_get_phase2_client_cert_path(setting);
if (!add_string_val(self, path, "client_cert2", FALSE, NULL, error))
return FALSE;
if (priv->private_user)
bytes = nm_g_hash_table_lookup(files, path);
break;
case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
if (!add_pkcs11_uri_with_pin(
@ -1886,6 +1927,19 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
default:
break;
}
if (bytes) {
if (!nm_supplicant_config_add_blob_for_connection(self,
bytes,
"client_cert2",
con_uuid,
error))
return FALSE;
} else if (path) {
/* Private connections cannot use paths */
g_return_val_if_fail(!priv->private_user, FALSE);
if (!add_string_val(self, path, "client_cert2", FALSE, NULL, error))
return FALSE;
}
}
}

View file

@ -29,7 +29,7 @@ typedef struct _NMSupplicantConfigClass NMSupplicantConfigClass;
GType nm_supplicant_config_get_type(void);
NMSupplicantConfig *nm_supplicant_config_new(NMSupplCapMask capabilities);
NMSupplicantConfig *nm_supplicant_config_new(NMSupplCapMask capabilities, const char *private_user);
guint32 nm_supplicant_config_get_ap_scan(NMSupplicantConfig *self);
@ -57,6 +57,7 @@ gboolean nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
guint32 mtu,
NMSettingWirelessSecurityPmf pmf,
NMSettingWirelessSecurityFils fils,
GHashTable *files,
GError **error);
gboolean nm_supplicant_config_add_no_security(NMSupplicantConfig *self, GError **error);
@ -66,6 +67,7 @@ gboolean nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
const char *con_uuid,
guint32 mtu,
gboolean wired,
GHashTable *files,
GError **error);
gboolean nm_supplicant_config_add_setting_macsec(NMSupplicantConfig *self,

View file

@ -46,6 +46,7 @@ typedef struct {
gpointer user_data;
guint fail_on_idle_id;
guint blobs_left;
guint remove_blobs_left;
guint calls_left;
struct _AddNetworkData *add_network_data;
} AssocData;
@ -2264,6 +2265,7 @@ assoc_add_blob_cb(GObject *source, GAsyncResult *result, gpointer user_data)
return;
}
nm_assert(priv->assoc_data->blobs_left > 0);
priv->assoc_data->blobs_left--;
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: blob added (%u left)",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
@ -2272,6 +2274,148 @@ assoc_add_blob_cb(GObject *source, GAsyncResult *result, gpointer user_data)
assoc_call_select_network(self);
}
static void
assoc_add_blobs(NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
GHashTable *blobs;
GHashTableIter iter;
const char *blob_name;
GBytes *blob_data;
blobs = nm_supplicant_config_get_blobs(priv->assoc_data->cfg);
priv->assoc_data->blobs_left = nm_g_hash_table_size(blobs);
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: need to add %u blobs",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
priv->assoc_data->blobs_left);
if (priv->assoc_data->blobs_left == 0) {
assoc_call_select_network(self);
return;
}
g_hash_table_iter_init(&iter, blobs);
while (g_hash_table_iter_next(&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: adding blob '%s'",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
blob_name);
_dbus_connection_call(
self,
NM_WPAS_DBUS_IFACE_INTERFACE,
"AddBlob",
g_variant_new("(s@ay)", blob_name, nm_g_bytes_to_variant_ay(blob_data)),
G_VARIANT_TYPE("()"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_MSEC,
priv->assoc_data->cancellable,
assoc_add_blob_cb,
self);
}
}
static void
assoc_remove_blob_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *res = NULL;
res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
if (nm_utils_error_is_cancelled(error))
return;
self = NM_SUPPLICANT_INTERFACE(user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
/* We don't consider a failure fatal. The new association might be able
* to proceed even with the existing blobs, if they don't conflict with new
* ones. */
nm_assert(priv->assoc_data->remove_blobs_left > 0);
priv->assoc_data->remove_blobs_left--;
if (error) {
g_dbus_error_strip_remote_error(error);
_LOGD("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: failed to delete blob: %s",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
error->message);
} else {
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: blob removed (%u left)",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
priv->assoc_data->remove_blobs_left);
}
if (priv->assoc_data->remove_blobs_left == 0)
assoc_add_blobs(self);
}
static void
assoc_get_blobs_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
NMSupplicantInterface *self;
NMSupplicantInterfacePrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *res = NULL;
gs_unref_variant GVariant *value = NULL;
GVariantIter iter;
const char *blob_name;
GVariant *blob_data;
res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
if (nm_utils_error_is_cancelled(error))
return;
self = NM_SUPPLICANT_INTERFACE(user_data);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
if (error) {
_LOGD("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: failed to get blob list: %s",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
error->message);
assoc_add_blobs(self);
return;
}
g_variant_get(res, "(v)", &value);
/* While the "Blobs" property is documented as type "as", it is actually "a{say}" */
if (!value || !g_variant_is_of_type(value, G_VARIANT_TYPE("a{say}"))) {
_LOGD("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: failed to get blob list: wrong return type %s",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
value ? g_variant_get_type_string(value) : "NULL");
assoc_add_blobs(self);
return;
}
g_variant_iter_init(&iter, value);
priv->assoc_data->remove_blobs_left = g_variant_iter_n_children(&iter);
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: need to delete %u blobs",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
priv->assoc_data->remove_blobs_left);
if (priv->assoc_data->remove_blobs_left == 0) {
assoc_add_blobs(self);
} else {
while (g_variant_iter_loop(&iter, "{&s@ay}", &blob_name, &blob_data)) {
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: removing blob '%s'",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
blob_name);
_dbus_connection_call(self,
NM_WPAS_DBUS_IFACE_INTERFACE,
"RemoveBlob",
g_variant_new("(s)", blob_name),
G_VARIANT_TYPE("()"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_MSEC,
priv->assoc_data->cancellable,
assoc_remove_blob_cb,
self);
}
}
}
static void
assoc_add_network_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
@ -2281,10 +2425,6 @@ assoc_add_network_cb(GObject *source, GAsyncResult *result, gpointer user_data)
NMSupplicantInterfacePrivate *priv;
gs_unref_variant GVariant *res = NULL;
gs_free_error GError *error = NULL;
GHashTable *blobs;
GHashTableIter iter;
const char *blob_name;
GBytes *blob_data;
nm_auto_ref_string NMRefString *name_owner = NULL;
nm_auto_ref_string NMRefString *object_path = NULL;
@ -2336,35 +2476,22 @@ assoc_add_network_cb(GObject *source, GAsyncResult *result, gpointer user_data)
nm_assert(!priv->net_path);
g_variant_get(res, "(o)", &priv->net_path);
/* Send blobs first; otherwise jump to selecting the network */
blobs = nm_supplicant_config_get_blobs(priv->assoc_data->cfg);
priv->assoc_data->blobs_left = blobs ? g_hash_table_size(blobs) : 0u;
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: network added (%s) (%u blobs left)",
_LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: network added (%s)",
NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
priv->net_path,
priv->assoc_data->blobs_left);
priv->net_path);
if (priv->assoc_data->blobs_left == 0) {
assoc_call_select_network(self);
return;
}
g_hash_table_iter_init(&iter, blobs);
while (g_hash_table_iter_next(&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
_dbus_connection_call(
self,
NM_WPAS_DBUS_IFACE_INTERFACE,
"AddBlob",
g_variant_new("(s@ay)", blob_name, nm_g_bytes_to_variant_ay(blob_data)),
G_VARIANT_TYPE("()"),
/* Delete any existing blobs before adding new ones */
_dbus_connection_call(self,
DBUS_INTERFACE_PROPERTIES,
"Get",
g_variant_new("(ss)", NM_WPAS_DBUS_IFACE_INTERFACE, "Blobs"),
G_VARIANT_TYPE("(v)"),
G_DBUS_CALL_FLAGS_NONE,
DBUS_TIMEOUT_MSEC,
priv->assoc_data->cancellable,
assoc_add_blob_cb,
assoc_get_blobs_cb,
self);
}
}
static void
add_network(NMSupplicantInterface *self)

View file

@ -98,7 +98,8 @@ build_supplicant_config(NMConnection *connection,
NMSetting8021x *s_8021x;
gboolean success;
config = nm_supplicant_config_new(capabilities);
config = nm_supplicant_config_new(capabilities,
nm_utils_get_connection_first_permissions_user(connection));
s_wifi = nm_connection_get_setting_wireless(connection);
g_assert(s_wifi);
@ -120,6 +121,7 @@ build_supplicant_config(NMConnection *connection,
mtu,
pmf,
fils,
NULL,
&error);
} else {
success = nm_supplicant_config_add_no_security(config, &error);

View file

@ -0,0 +1,3 @@
# Good configuration, an empty global-dns section must be valid
[global-dns]

View file

@ -0,0 +1,5 @@
# Good configuration, an empty [global-dns] must be implicitly assumed because a domain is defined
[global-dns-domain-*]
servers=4.5.6.7
options=myoption1

View file

@ -387,7 +387,27 @@ test_config_global_dns(void)
g_assert(dns);
g_object_unref(config);
/* Check that a file with a domain domain, but without a default one gives a NULL configuration */
/* Check that a file with an empty global-dns section gives a good configuration.
* Check also that searches and options are not NULL, as this is how we expose to
* D-Bus that global-dns is defined. */
config =
setup_config(NULL, TEST_DIR "/global-dns-empty.conf", "", NULL, "/no/such/dir", "", NULL);
dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config));
g_assert(dns);
g_assert(nm_global_dns_config_get_searches(dns));
g_assert(nm_global_dns_config_get_options(dns));
g_object_unref(config);
/* Check that a file with a domain, but no global-dns, assumes an implicit empty global-dns */
config =
setup_config(NULL, TEST_DIR "/global-dns-not-set.conf", "", NULL, "/no/such/dir", "", NULL);
dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config));
g_assert(dns);
g_assert(nm_global_dns_config_get_searches(dns));
g_assert(nm_global_dns_config_get_options(dns));
g_object_unref(config);
/* Check that a file with a domain, but without a default one, gives a NULL configuration */
config =
setup_config(NULL, TEST_DIR "/global-dns-invalid.conf", "", NULL, "/no/such/dir", "", NULL);
dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config));

View file

@ -2076,3 +2076,16 @@ global:
nm_setting_sriov_get_preserve_on_down;
nm_sriov_preserve_on_down_get_type;
} libnm_1_52_0;
libnm_1_54_2 {
global:
nm_setting_hsr_get_interlink;
nm_setting_hsr_get_protocol_version;
nm_setting_hsr_protocol_version_get_type;
} libnm_1_54_0;
libnm_1_54_3 {
global:
nm_utils_copy_cert_as_user;
nm_vpn_plugin_info_supports_safe_private_file_access;
} libnm_1_54_2;

View file

@ -5,6 +5,7 @@ test_units = [
'test-nm-client',
'test-remote-settings-client',
'test-secret-agent',
'test-copy-cert-as-user'
]
foreach test_unit: test_units
@ -37,12 +38,15 @@ foreach test_unit: test_units
],
)
# test-copy-cert-as-user is a manual test, don't run it automatically
if test_unit != 'test-copy-cert-as-user'
test(
'src/libnm-client-impl/tests/' + test_unit,
test_script,
timeout: 90,
args: test_args + [exe.full_path()],
)
endif
endforeach
if enable_introspection

View file

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* This is a program to manually test the
* nm_utils_copy_cert_as_user() libnm function.
*/
#include "libnm-client-impl/nm-default-libnm.h"
#include "nm-utils.h"
int
main(int argc, char **argv)
{
gs_free_error GError *error = NULL;
gs_free char *filename = NULL;
if (argc != 3) {
g_printerr("Usage: %s <FILE> <USER>\n", argv[0]);
return 1;
}
filename = nm_utils_copy_cert_as_user(argv[1], argv[2], &error);
if (!filename) {
g_printerr("Error: %s\n", error->message);
return 1;
}
g_print("%s\n", filename);
return 0;
}

View file

@ -93,6 +93,8 @@ def syms_from_ver(verfile):
# hardcode it.
c_syms["nm_ethtool_optname_is_feature"] = "1.20"
c_syms["nm_setting_bond_port_get_prio"] = "1.44"
c_syms["nm_utils_copy_cert_as_user"] = "1.56"
c_syms["nm_vpn_plugin_info_supports_safe_private_file_access"] = "1.56"
return c_syms

View file

@ -1434,7 +1434,7 @@ test_client_wait_shutdown(void)
for (i_run = 0; i_run < N_RUN; i_run++) {
gs_unref_object GCancellable *init_cancellable = g_cancellable_new();
gs_unref_object NMClient *nmc = NULL;
nm_auto_pop_gmaincontext GMainContext *client_context = NULL;
nm_auto_pop_and_unref_gmaincontext GMainContext *client_context = NULL;
gboolean b;
gboolean context_integrated = FALSE;
gs_unref_object GCancellable *cancellable_1 = NULL;

View file

@ -1516,6 +1516,10 @@
<setting name="hsr"
gtype="NMSettingHsr"
>
<property name="interlink"
dbus-type="s"
gprop-type="gchararray"
/>
<property name="multicast-spec"
dbus-type="u"
gprop-type="guint"
@ -1528,6 +1532,10 @@
dbus-type="s"
gprop-type="gchararray"
/>
<property name="protocol-version"
dbus-type="i"
gprop-type="gint"
/>
<property name="prp"
dbus-type="b"
gprop-type="gboolean"

View file

@ -3133,6 +3133,86 @@ need_secrets(NMSetting *setting, gboolean check_rerequest)
/*****************************************************************************/
static void
get_private_files(NMSetting *setting, GPtrArray *files)
{
const struct {
const char *property;
NMSetting8021xCKScheme (*get_scheme_func)(NMSetting8021x *);
const char *(*get_path_func)(NMSetting8021x *);
} cert_props[] = {
{NM_SETTING_802_1X_CA_CERT,
nm_setting_802_1x_get_ca_cert_scheme,
nm_setting_802_1x_get_ca_cert_path},
{NM_SETTING_802_1X_CLIENT_CERT,
nm_setting_802_1x_get_client_cert_scheme,
nm_setting_802_1x_get_client_cert_path},
{NM_SETTING_802_1X_PRIVATE_KEY,
nm_setting_802_1x_get_private_key_scheme,
nm_setting_802_1x_get_private_key_path},
{NM_SETTING_802_1X_PHASE2_CA_CERT,
nm_setting_802_1x_get_phase2_ca_cert_scheme,
nm_setting_802_1x_get_phase2_ca_cert_path},
{NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
nm_setting_802_1x_get_phase2_client_cert_scheme,
nm_setting_802_1x_get_phase2_client_cert_path},
{NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
nm_setting_802_1x_get_phase2_private_key_scheme,
nm_setting_802_1x_get_phase2_private_key_path},
};
NMSetting8021x *s_8021x = NM_SETTING_802_1X(setting);
const char *path;
guint i;
if (NM_MORE_ASSERT_ONCE(5)) {
GObjectClass *klass;
gs_free GParamSpec **properties = NULL;
guint n_properties;
gboolean found;
guint j;
/* Check that all the properties in the setting with flag CERT_KEY_FILE
* are listed in the table, and vice versa. */
klass = G_OBJECT_GET_CLASS(setting);
properties = g_object_class_list_properties(klass, &n_properties);
for (i = 0; i < n_properties; i++) {
if (!(properties[i]->flags & NM_SETTING_PARAM_CERT_KEY_FILE))
continue;
found = FALSE;
for (j = 0; j < G_N_ELEMENTS(cert_props); j++) {
if (nm_streq0(properties[i]->name, cert_props[j].property)) {
found = TRUE;
break;
}
}
nm_assert(found);
}
for (i = 0; i < G_N_ELEMENTS(cert_props); i++) {
GParamSpec *prop;
prop = g_object_class_find_property(klass, cert_props[i].property);
nm_assert(prop);
nm_assert(prop->flags & NM_SETTING_PARAM_CERT_KEY_FILE);
}
}
for (i = 0; i < G_N_ELEMENTS(cert_props); i++) {
if (cert_props[i].get_scheme_func(s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
path = cert_props[i].get_path_func(s_8021x);
if (path) {
g_ptr_array_add(files, (gpointer) path);
}
}
}
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
@ -3225,6 +3305,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
setting_class->verify = verify;
setting_class->need_secrets = need_secrets;
setting_class->get_private_files = get_private_files;
/**
* NMSetting8021x:eap:
@ -3359,7 +3440,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_CA_CERT,
PROP_CA_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
ca_cert);
@ -3556,7 +3637,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_CLIENT_CERT,
PROP_CLIENT_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
client_cert);
@ -3803,7 +3884,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PHASE2_CA_CERT,
PROP_PHASE2_CA_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
phase2_ca_cert);
@ -4006,7 +4087,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
PROP_PHASE2_CLIENT_CERT,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
phase2_client_cert);
@ -4175,7 +4256,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PRIVATE_KEY,
PROP_PRIVATE_KEY,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
private_key);
@ -4276,7 +4357,7 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
obj_properties,
NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
PROP_PHASE2_PRIVATE_KEY,
NM_SETTING_PARAM_NONE,
NM_SETTING_PARAM_CERT_KEY_FILE,
NMSetting8021xPrivate,
phase2_private_key);

View file

@ -431,6 +431,47 @@ nm_setting_connection_permissions_user_allowed_by_uid(NMSettingConnection *setti
return _permissions_user_allowed(setting, NULL, uid);
}
guint
_nm_setting_connection_get_num_permissions_users(NMSettingConnection *setting)
{
NMSettingConnectionPrivate *priv;
guint i;
guint count = 0;
nm_assert(NM_IS_SETTING_CONNECTION(setting));
priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
for (i = 0; priv->permissions && i < priv->permissions->len; i++) {
const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i);
if (permission->ptype == PERM_TYPE_USER) {
count++;
}
}
return count;
}
const char *
_nm_setting_connection_get_first_permissions_user(NMSettingConnection *setting)
{
NMSettingConnectionPrivate *priv;
guint i;
nm_assert(NM_IS_SETTING_CONNECTION(setting));
priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
for (i = 0; priv->permissions && i < priv->permissions->len; i++) {
const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i);
if (permission->ptype == PERM_TYPE_USER) {
return permission->item;
}
}
return NULL;
}
/**
* nm_setting_connection_add_permission:
* @setting: the #NMSettingConnection

View file

@ -23,12 +23,20 @@
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingHsr, PROP_PORT1, PROP_PORT2, PROP_MULTICAST_SPEC, PROP_PRP, );
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingHsr,
PROP_PORT1,
PROP_PORT2,
PROP_MULTICAST_SPEC,
PROP_PRP,
PROP_PROTOCOL_VERSION,
PROP_INTERLINK, );
typedef struct {
char *port1;
char *port2;
char *interlink;
guint32 multicast_spec;
int protocol_version;
bool prp;
} NMSettingHsrPrivate;
@ -117,6 +125,38 @@ nm_setting_hsr_get_prp(NMSettingHsr *setting)
return NM_SETTING_HSR_GET_PRIVATE(setting)->prp;
}
/**
* nm_setting_hsr_get_protocol_version:
* @setting: the #NMSettingHsr
*
* Returns: the #NMSettingHsr:protocol-version property of the setting
*
* Since: 1.56, 1.54.2
**/
NMSettingHsrProtocolVersion
nm_setting_hsr_get_protocol_version(NMSettingHsr *setting)
{
g_return_val_if_fail(NM_IS_SETTING_HSR(setting), NM_SETTING_HSR_PROTOCOL_VERSION_DEFAULT);
return NM_SETTING_HSR_GET_PRIVATE(setting)->protocol_version;
}
/**
* nm_setting_hsr_get_interlink:
* @setting: the #NMSettingHsr
*
* Returns: the #NMSettingHsr:interlink property of the setting
*
* Since: 1.56, 1.54.2
**/
const char *
nm_setting_hsr_get_interlink(NMSettingHsr *setting)
{
g_return_val_if_fail(NM_IS_SETTING_HSR(setting), NULL);
return NM_SETTING_HSR_GET_PRIVATE(setting)->interlink;
}
/*****************************************************************************/
static gboolean
@ -160,6 +200,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
if (priv->prp && priv->protocol_version != NM_SETTING_HSR_PROTOCOL_VERSION_DEFAULT) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("HSR protocol cannot be configured for PRP interfaces"));
g_prefix_error(error,
"%s.%s: ",
NM_SETTING_HSR_SETTING_NAME,
NM_SETTING_HSR_PROTOCOL_VERSION);
return FALSE;
}
return TRUE;
}
@ -260,6 +312,42 @@ nm_setting_hsr_class_init(NMSettingHsrClass *klass)
NMSettingHsr,
_priv.prp);
/**
* NMSettingHsr:protocol-version:
*
* Configures the protocol version to be used for the HSR/PRP interface.
* %NM_SETTING_HSR_PROTOCOL_VERSION_DEFAULT sets the protocol version to the default version for the protocol.
* %NM_SETTING_HSR_PROTOCOL_VERSION_HSR_2010 sets the protocol version to HSRv0 (IEC 62439-3:2010).
* %NM_SETTING_HSR_PROTOCOL_VERSION_HSR_2012 sets the protocol version to HSRv1 (IEC 62439-3:2012).
*
* Since: 1.56, 1.54.2
**/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_HSR_PROTOCOL_VERSION,
PROP_PROTOCOL_VERSION,
NM_TYPE_SETTING_HSR_PROTOCOL_VERSION,
NM_SETTING_HSR_PROTOCOL_VERSION_DEFAULT,
NM_SETTING_PARAM_NONE,
NULL,
NMSettingHsr,
_priv.protocol_version);
/**
* NMSettingHsr:interlink:
*
* The optional interlink port name of the HSR interface.
*
* Since: 1.56, 1.54.2
**/
_nm_setting_property_define_direct_string(properties_override,
obj_properties,
NM_SETTING_HSR_INTERLINK,
PROP_INTERLINK,
NM_SETTING_PARAM_INFERRABLE,
NMSettingHsr,
_priv.interlink);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_HSR, NULL, properties_override, 0);

View file

@ -154,6 +154,11 @@ struct _NMSettingClass {
guint /* NMSettingParseFlags */ parse_flags,
GError **error);
/* returns a list of certificate/key files referenced in the connection.
* When the connection is private, we need to verify that the owner of
* the connection has access to them. */
void (*get_private_files)(NMSetting *setting, GPtrArray *files);
const struct _NMMetaSettingInfo *setting_info;
};
@ -334,6 +339,11 @@ struct _NMRange {
*/
#define NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS (1 << (7 + G_PARAM_USER_SHIFT))
/* The property can refer to a certificate or key stored on disk. As such,
* special care is needed when accessing the file for private connections.
*/
#define NM_SETTING_PARAM_CERT_KEY_FILE (1 << (8 + G_PARAM_USER_SHIFT))
extern const NMSettInfoPropertType nm_sett_info_propert_type_setting_name;
extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name;
extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_i;
@ -859,9 +869,10 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
{ \
GParamSpec *_param_spec; \
\
G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags), \
G_STATIC_ASSERT( \
!NM_FLAGS_ANY((param_flags), \
~(NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_INFERRABLE \
| NM_SETTING_PARAM_FUZZY_IGNORE))); \
| NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_CERT_KEY_FILE))); \
\
_param_spec = g_param_spec_boxed("" prop_name "", \
"", \

View file

@ -1477,6 +1477,7 @@ peers_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
for (i_peer = 0; i_peer < n_peers; i_peer++) {
const NMWireGuardPeer *peer = _peers_get(priv, i_peer)->peer;
GVariantBuilder builder;
gboolean has_secrets = FALSE;
if (!peer->public_key)
continue;
@ -1496,11 +1497,13 @@ peers_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
g_variant_new_string(nm_sock_addr_endpoint_get_endpoint(peer->endpoint)));
if (_nm_connection_serialize_secrets(flags, peer->preshared_key_flags)
&& peer->preshared_key)
&& peer->preshared_key) {
g_variant_builder_add(&builder,
"{sv}",
NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY,
g_variant_new_string(peer->preshared_key));
has_secrets = TRUE;
}
if (_nm_connection_serialize_non_secret(flags)
&& peer->preshared_key_flags != NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
@ -1546,6 +1549,20 @@ peers_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
g_variant_new_strv(strv, peer->allowed_ips->len));
}
if (NM_FLAGS_ANY(flags,
NM_CONNECTION_SERIALIZE_ONLY_SECRETS
| NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED
| NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED
| NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)
&& !_nm_connection_serialize_non_secret(flags)) {
/* The flags indicate that only secrets must be serialized and this
* peer doesn't contain any. Skip the peer. */
if (!has_secrets) {
g_variant_builder_clear(&builder);
continue;
}
}
if (!any_peers) {
g_variant_builder_init(&peers_builder, G_VARIANT_TYPE("aa{sv}"));
any_peers = TRUE;

View file

@ -2262,6 +2262,34 @@ init_from_dbus(NMSetting *setting,
return TRUE;
}
static void
get_private_files(NMSetting *setting, GPtrArray *files)
{
if (NM_MORE_ASSERTS) {
GParamSpec **properties;
guint n_properties;
int i;
properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(setting), &n_properties);
for (i = 0; i < n_properties; i++) {
if (properties[i]->flags & NM_SETTING_PARAM_CERT_KEY_FILE) {
/* Certificates and keys needs special handling, see setting 802.1X */
nm_assert_not_reached();
}
}
g_free(properties);
}
}
void
_nm_setting_get_private_files(NMSetting *setting, GPtrArray *files)
{
g_return_if_fail(NM_IS_SETTING(setting));
g_return_if_fail(files);
NM_SETTING_GET_CLASS(setting)->get_private_files(setting, files);
}
/**
* nm_setting_get_dbus_property_type:
* @setting: an #NMSetting
@ -4672,6 +4700,7 @@ nm_setting_class_init(NMSettingClass *setting_class)
setting_class->enumerate_values = enumerate_values;
setting_class->aggregate = aggregate;
setting_class->init_from_dbus = init_from_dbus;
setting_class->get_private_files = get_private_files;
/**
* NMSetting:name:

View file

@ -17,6 +17,7 @@
#include <linux/pkt_sched.h>
#include <linux/if_infiniband.h>
#include "libnm-glib-aux/nm-io-utils.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "libnm-glib-aux/nm-json-aux.h"
#include "libnm-glib-aux/nm-str-buf.h"
@ -6195,3 +6196,257 @@ nm_utils_ensure_gtypes(void)
for (meta_type = 0; meta_type < _NM_META_SETTING_TYPE_NUM; meta_type++)
nm_meta_setting_infos[meta_type].get_setting_gtype();
}
/*****************************************************************************/
typedef struct {
GPid pid;
GSource *child_watch_source;
GMainLoop *loop;
GError *error;
int child_stdout;
int child_stderr;
GSource *output_source;
GSource *error_source;
NMStrBuf output_buffer;
NMStrBuf error_buffer;
} HelperInfo;
static void
helper_complete(HelperInfo *info, GError *error_take)
{
if (error_take) {
if (!info->error)
info->error = error_take;
else
g_error_free(error_take);
}
if (info->output_source || info->error_source || info->pid != -1) {
/* Wait that the pipe is closed and process has terminated */
return;
}
if (info->error && info->error_buffer.len > 0) {
/* Prefer the message from stderr as it's more informative */
g_error_free(info->error);
info->error = g_error_new(NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
"%s",
nm_str_buf_get_str(&info->error_buffer));
}
g_main_loop_quit(info->loop);
}
static gboolean
helper_have_err_data(int fd, GIOCondition condition, gpointer user_data)
{
HelperInfo *info = user_data;
gssize n_read;
GError *error = NULL;
n_read = nm_utils_fd_read(fd, &info->error_buffer);
if (n_read > 0)
return G_SOURCE_CONTINUE;
nm_clear_g_source_inst(&info->error_source);
nm_clear_fd(&info->child_stderr);
if (n_read < 0) {
error = g_error_new(NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"read from process returned %d (%s)",
(int) -n_read,
nm_strerror_native((int) -n_read));
}
helper_complete(info, error);
return G_SOURCE_CONTINUE;
}
static gboolean
helper_have_data(int fd, GIOCondition condition, gpointer user_data)
{
HelperInfo *info = user_data;
gssize n_read;
GError *error = NULL;
n_read = nm_utils_fd_read(fd, &info->output_buffer);
if (n_read > 0)
return G_SOURCE_CONTINUE;
nm_clear_g_source_inst(&info->output_source);
nm_clear_fd(&info->child_stdout);
if (n_read < 0) {
error = g_error_new(NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"read from process returned %d (%s)",
(int) -n_read,
nm_strerror_native((int) -n_read));
}
helper_complete(info, error);
return G_SOURCE_CONTINUE;
}
static void
helper_child_terminated(GPid pid, int status, gpointer user_data)
{
HelperInfo *info = user_data;
gs_free char *status_desc = NULL;
GError *error = NULL;
info->pid = -1;
nm_clear_g_source_inst(&info->child_watch_source);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (!status_desc)
status_desc = nm_utils_get_process_exit_status_desc(status);
error =
g_error_new(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "helper process %s", status_desc);
}
helper_complete(info, error);
}
#define RUN_CERT_DIR NMRUNDIR "/cert"
/**
* nm_utils_copy_cert_as_user:
* @filename: the file name of the certificate or key to copy
* @user: the user to impersonate when reading the file
* @error: (nullable): return location for a #GError, or %NULL
*
* Reads @filename on behalf of user @user and writes the
* content to a new file in /run/NetworkManager/cert/.
* The new file has permission 600 and is owned by root.
*
* This function is useful for VPN plugins that run as root and need
* to verify that the user owning the connection (the one listed in the
* connection.permissions property) can access the file.
*
* Returns: (transfer full): the name of the new temporary file. Or %NULL
* if an error occurred, including when the given user can't access the
* file.
*
* Since: 1.56, 1.54.3
*/
char *
nm_utils_copy_cert_as_user(const char *filename, const char *user, GError **error)
{
gs_unref_bytes GBytes *bytes = NULL;
char dst_path[] = RUN_CERT_DIR "/XXXXXX";
HelperInfo info = {
.child_stdout = -1,
.child_stderr = -1,
};
GMainContext *context;
int fd = -1;
g_return_val_if_fail(filename, NULL);
g_return_val_if_fail(user, NULL);
g_return_val_if_fail(!error || !*error, NULL);
if (geteuid() != 0) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("This function needs to be called by root"));
return NULL;
}
if (!g_spawn_async_with_pipes(
"/",
(char **)
NM_MAKE_STRV(LIBEXECDIR "/nm-libnm-helper", "read-file-as-user", filename, user),
(char **) NM_MAKE_STRV(),
G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&info.pid,
NULL,
&info.child_stdout,
&info.child_stderr,
error)) {
return NULL;
}
context = g_main_context_new();
info.loop = g_main_loop_new(context, FALSE);
/* Watch process */
info.child_watch_source = nm_g_child_watch_source_new(info.pid,
G_PRIORITY_DEFAULT,
helper_child_terminated,
&info,
NULL);
g_source_attach(info.child_watch_source, context);
/* Watch stdout */
info.output_buffer = NM_STR_BUF_INIT(0, FALSE);
info.output_source = nm_g_unix_fd_source_new(info.child_stdout,
G_IO_IN | G_IO_ERR | G_IO_HUP,
G_PRIORITY_DEFAULT,
helper_have_data,
&info,
NULL);
g_source_attach(info.output_source, context);
/* Watch stderr */
info.error_buffer = NM_STR_BUF_INIT(0, FALSE);
info.error_source = nm_g_unix_fd_source_new(info.child_stderr,
G_IO_IN | G_IO_ERR | G_IO_HUP,
G_PRIORITY_DEFAULT,
helper_have_err_data,
&info,
NULL);
g_source_attach(info.error_source, context);
/* Wait termination */
g_main_loop_run(info.loop);
g_clear_pointer(&info.loop, g_main_loop_unref);
g_clear_pointer(&context, g_main_context_unref);
if (info.error) {
nm_str_buf_destroy(&info.output_buffer);
nm_str_buf_destroy(&info.error_buffer);
g_propagate_error(error, g_steal_pointer(&info.error));
return NULL;
}
/* Write the data to a new file */
bytes = g_bytes_new(nm_str_buf_get_str_unsafe(&info.output_buffer), info.output_buffer.len);
nm_str_buf_destroy(&info.output_buffer);
nm_str_buf_destroy(&info.error_buffer);
mkdir(RUN_CERT_DIR, 0600);
fd = mkstemp(dst_path);
if (fd < 0) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("Failure creating the temporary file"));
return NULL;
}
nm_close(fd);
if (!nm_utils_file_set_contents(dst_path,
g_bytes_get_data(bytes, NULL),
g_bytes_get_size(bytes),
0600,
NULL,
NULL,
error)) {
return NULL;
}
return g_strdup(dst_path);
}

View file

@ -913,6 +913,29 @@ nm_vpn_plugin_info_supports_multiple(NMVpnPluginInfo *self)
return _nm_utils_ascii_str_to_bool(s, FALSE);
}
/**
* nm_vpn_plugin_info_supports_safe_private_file_access:
* @self: plugin info instance
*
* Returns: %TRUE if the service supports reading files (certificates, keys) of
* private connections in a safe way (i.e. checking user permissions), or
if the service doesn't need to read any file from disk.
*
* Since: 1.56, 1.54.3
*/
gboolean
nm_vpn_plugin_info_supports_safe_private_file_access(NMVpnPluginInfo *self)
{
const char *s;
g_return_val_if_fail(NM_IS_VPN_PLUGIN_INFO(self), FALSE);
s = nm_vpn_plugin_info_lookup_property(self,
NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION,
"supports-safe-private-file-access");
return _nm_utils_ascii_str_to_bool(s, FALSE);
}
/**
* nm_vpn_plugin_info_get_aliases:
* @self: plugin info instance

View file

@ -5522,6 +5522,223 @@ test_bond_meta(void)
/*****************************************************************************/
static void
check_wg_setting_str(NMSetting *s_wg,
const char *exp_all,
const char *exp_nonsec,
const char *exp_sec)
{
gs_unref_variant GVariant *dict_all = NULL;
gs_unref_variant GVariant *dict_nonsec = NULL;
gs_unref_variant GVariant *dict_sec = NULL;
gs_free char *str_all = NULL;
gs_free char *str_nonsec = NULL;
gs_free char *str_sec = NULL;
dict_all = _nm_setting_to_dbus(s_wg, NULL, NM_CONNECTION_SERIALIZE_ALL, NULL);
dict_nonsec = _nm_setting_to_dbus(s_wg, NULL, NM_CONNECTION_SERIALIZE_WITH_NON_SECRET, NULL);
dict_sec = _nm_setting_to_dbus(s_wg, NULL, NM_CONNECTION_SERIALIZE_ONLY_SECRETS, NULL);
str_all = g_variant_print(dict_all, TRUE);
str_nonsec = g_variant_print(dict_nonsec, TRUE);
str_sec = g_variant_print(dict_sec, TRUE);
g_assert_cmpstr(exp_all, ==, str_all);
g_assert_cmpstr(exp_nonsec, ==, str_nonsec);
g_assert_cmpstr(exp_sec, ==, str_sec);
}
static void
test_wireguard_to_dbus(void)
{
gs_unref_object NMSetting *s_wg = NULL;
nm_auto_unref_wgpeer NMWireGuardPeer *peer1 = NULL;
nm_auto_unref_wgpeer NMWireGuardPeer *peer2 = NULL;
gs_unref_variant GVariant *dict_all = NULL;
gs_unref_variant GVariant *dict_non_secret = NULL;
gs_unref_variant GVariant *dict_only_secrets = NULL;
gs_free char *dict_str = NULL;
const char *test_private_key = "cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM=";
const char *test_public_key1 = "OMhgSum5+NamArI/LTp1mCZQD+CbzZxtOuvDC/RaGWU=";
const char *test_public_key2 = "2S7mA0vEMethVGG0qBm4T5EXbcQ2WYHOuP14Seb7jEM=";
const char *test_preshared_key = "yFGq76ej4lNI0pLLu36L0DgJMxWs4HmH5qNDNOt8AmM=";
/* Test case 1: Minimal WireGuard setting without peers or private key */
s_wg = nm_setting_wireguard_new();
g_object_set(s_wg,
NM_SETTING_WIREGUARD_LISTEN_PORT,
51820U,
NM_SETTING_WIREGUARD_FWMARK,
42U,
NULL);
check_wg_setting_str(s_wg,
/* clang-format off */
/* all */
"{'fwmark': <uint32 42>, 'listen-port': <uint32 51820>}",
/* non secrets */
"{'fwmark': <uint32 42>, 'listen-port': <uint32 51820>}",
/* secrets */
"@a{sv} {}"
/* clang-format on */
);
g_clear_object(&s_wg);
/* Test case 2: WireGuard setting with private key, no peers */
s_wg = nm_setting_wireguard_new();
g_object_set(s_wg,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
test_private_key,
NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_WIREGUARD_LISTEN_PORT,
51820U,
NM_SETTING_WIREGUARD_FWMARK,
42U,
NULL);
check_wg_setting_str(s_wg,
/* clang-format off */
/* all */
"{"
"'fwmark': <uint32 42>, "
"'listen-port': <uint32 51820>, "
"'private-key': <'cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM='>"
"}",
/* non secrets */
"{"
"'fwmark': <uint32 42>, "
"'listen-port': <uint32 51820>"
"}",
/* secrets */
"{"
"'private-key': <'cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM='>"
"}"
/* clang-format on */
);
g_clear_object(&s_wg);
/* Test case 3: WireGuard setting with peers (no PSK) */
s_wg = nm_setting_wireguard_new();
g_object_set(s_wg,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
test_private_key,
NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_WIREGUARD_LISTEN_PORT,
51820U,
NULL);
peer1 = nm_wireguard_peer_new();
nm_wireguard_peer_set_public_key(peer1, test_public_key1, FALSE);
nm_wireguard_peer_set_endpoint(peer1, "192.168.1.1:51820", FALSE);
nm_wireguard_peer_append_allowed_ip(peer1, "10.0.0.0/8", FALSE);
nm_setting_wireguard_append_peer(NM_SETTING_WIREGUARD(s_wg), peer1);
check_wg_setting_str(s_wg,
/* clang-format off */
/* all */
"{"
"'listen-port': <uint32 51820>, "
"'peers': <[{"
"'public-key': <'OMhgSum5+NamArI/LTp1mCZQD+CbzZxtOuvDC/RaGWU='>, "
"'endpoint': <'192.168.1.1:51820'>, "
"'allowed-ips': <['10.0.0.0/8']>"
"}]>, "
"'private-key': <'cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM='>"
"}",
/* non secrets */
"{"
"'listen-port': <uint32 51820>, "
"'peers': <[{"
"'public-key': <'OMhgSum5+NamArI/LTp1mCZQD+CbzZxtOuvDC/RaGWU='>, "
"'endpoint': <'192.168.1.1:51820'>, "
"'allowed-ips': <['10.0.0.0/8']>"
"}]>"
"}",
/* secrets */
"{"
"'private-key': <'cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM='>"
"}"
/* clang-format on */
);
g_clear_object(&s_wg);
nm_clear_pointer(&peer1, nm_wireguard_peer_unref);
/* Test case 4: WireGuard setting with peers, one has PSK */
s_wg = nm_setting_wireguard_new();
g_object_set(s_wg,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
test_private_key,
NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_WIREGUARD_LISTEN_PORT,
51820U,
NULL);
/* Peer without PSK */
peer1 = nm_wireguard_peer_new();
nm_wireguard_peer_set_public_key(peer1, test_public_key1, FALSE);
nm_wireguard_peer_set_endpoint(peer1, "192.168.1.1:51820", FALSE);
nm_wireguard_peer_append_allowed_ip(peer1, "10.0.0.0/8", FALSE);
nm_setting_wireguard_append_peer(NM_SETTING_WIREGUARD(s_wg), peer1);
/* Peer with PSK */
peer2 = nm_wireguard_peer_new();
nm_wireguard_peer_set_public_key(peer2, test_public_key2, FALSE);
nm_wireguard_peer_set_endpoint(peer2, "192.168.2.1:51820", FALSE);
nm_wireguard_peer_append_allowed_ip(peer2, "172.16.0.0/12", FALSE);
nm_wireguard_peer_set_preshared_key(peer2, test_preshared_key, FALSE);
nm_wireguard_peer_set_preshared_key_flags(peer2, NM_SETTING_SECRET_FLAG_NONE);
nm_setting_wireguard_append_peer(NM_SETTING_WIREGUARD(s_wg), peer2);
check_wg_setting_str(s_wg,
/* clang-format off */
/* all */
"{"
"'listen-port': <uint32 51820>, "
"'peers': <[{"
"'public-key': <'OMhgSum5+NamArI/LTp1mCZQD+CbzZxtOuvDC/RaGWU='>, "
"'endpoint': <'192.168.1.1:51820'>, "
"'allowed-ips': <['10.0.0.0/8']>"
"}, {"
"'public-key': <'2S7mA0vEMethVGG0qBm4T5EXbcQ2WYHOuP14Seb7jEM='>, "
"'endpoint': <'192.168.2.1:51820'>, "
"'preshared-key': <'yFGq76ej4lNI0pLLu36L0DgJMxWs4HmH5qNDNOt8AmM='>, "
"'preshared-key-flags': <uint32 0>, "
"'allowed-ips': <['172.16.0.0/12']>"
"}]>, "
"'private-key': <'cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM='>"
"}",
/* non secrets */
"{"
"'listen-port': <uint32 51820>, "
"'peers': <[{"
"'public-key': <'OMhgSum5+NamArI/LTp1mCZQD+CbzZxtOuvDC/RaGWU='>, "
"'endpoint': <'192.168.1.1:51820'>, "
"'allowed-ips': <['10.0.0.0/8']>"
"}, {"
"'public-key': <'2S7mA0vEMethVGG0qBm4T5EXbcQ2WYHOuP14Seb7jEM='>, "
"'endpoint': <'192.168.2.1:51820'>, "
"'preshared-key-flags': <uint32 0>, "
"'allowed-ips': <['172.16.0.0/12']>"
"}]>"
"}",
/* secrets */
"{"
"'peers': <[{"
"'public-key': <'2S7mA0vEMethVGG0qBm4T5EXbcQ2WYHOuP14Seb7jEM='>, "
"'preshared-key': <'yFGq76ej4lNI0pLLu36L0DgJMxWs4HmH5qNDNOt8AmM='>"
"}]>, "
"'private-key': <'cFoJbK9bSrYrQrjFQGgqsWTO4IUIX0+rsaqNeCw2IWM='>}"
/* clang-format on */
);
g_clear_object(&s_wg);
nm_clear_pointer(&peer1, nm_wireguard_peer_unref);
nm_clear_pointer(&peer2, nm_wireguard_peer_unref);
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -5649,5 +5866,7 @@ main(int argc, char **argv)
g_test_add_func("/libnm/test_bond_meta", test_bond_meta);
g_test_add_func("/libnm/test_wireguard_to_dbus", test_wireguard_to_dbus);
return g_test_run();
}

View file

@ -1187,4 +1187,11 @@ gboolean nm_connection_need_secrets_for_rerequest(NMConnection *connection);
const GPtrArray *_nm_setting_ovs_port_get_trunks_arr(NMSettingOvsPort *self);
/*****************************************************************************/
guint _nm_setting_connection_get_num_permissions_users(NMSettingConnection *setting);
const char *_nm_setting_connection_get_first_permissions_user(NMSettingConnection *setting);
void _nm_setting_get_private_files(NMSetting *setting, GPtrArray *files);
#endif

View file

@ -145,8 +145,10 @@ typedef enum {
* and not disable controls that require network access.
* The graphical shells may hide the network accessibility indicator altogether
* since no meaningful status indication can be provided.
* @NM_STATE_ASLEEP: Networking is not enabled, the system is being suspended or
* resumed from suspend.
* @NM_STATE_ASLEEP: Deprecated: 1.56: Use %NM_STATE_DISABLED instead.
* @NM_STATE_DISABLED: NetworkManager is disabled, either because the user requested
* to disable networking or because the system is suspended or resuming from suspend.
* Since: 1.56.
* @NM_STATE_DISCONNECTED: There is no active network connection.
* The graphical shell should indicate no network connectivity and the
* applications should not attempt to access the network.
@ -170,7 +172,8 @@ typedef enum {
**/
typedef enum {
NM_STATE_UNKNOWN = 0,
NM_STATE_ASLEEP = 10,
NM_STATE_ASLEEP = 10, /* Deprecated */
NM_STATE_DISABLED = 10,
NM_STATE_DISCONNECTED = 20,
NM_STATE_DISCONNECTING = 30,
NM_STATE_CONNECTING = 40,
@ -632,8 +635,10 @@ typedef enum {
* not initialized by udev. Since: 1.48
* @NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING: The device is unmanaged because NetworkManager is
* quitting. Since: 1.48
* @NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING: The device is unmanaged because networking is
* disabled or the system is suspended. Since: 1.48
* @NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING: Since: 1.48. Deprecated: 1.56: Use
* %NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED instead.
* @NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED: The device is unmanaged because networking is
* disabled or the system is suspended. Since: 1.56
* @NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF: The device is unmanaged by user decision in
* NetworkManager.conf ('unmanaged' in a [device*] section). Since: 1.48
* @NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT: The device is unmanaged by explicit user
@ -642,7 +647,7 @@ typedef enum {
* via settings plugin ('unmanaged-devices' for keyfile or 'NM_CONTROLLED=no' for ifcfg-rh).
* Since: 1.48
* @NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV: The device is unmanaged via udev rule. Since: 1.48
* @NM_DEVICE_STATE_REASON_NETWORKING_OFF: NetworkManager was disabled (networking off). Since: 1.56
*
* Device state change reason codes
*/
@ -720,11 +725,13 @@ typedef enum {
NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN = 70,
NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT = 71,
NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING = 72,
NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING = 73,
NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING = 73, /* Deprecated */
NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED = 73,
NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF = 74,
NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT = 75,
NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS = 76,
NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV = 77,
NM_DEVICE_STATE_REASON_NETWORKING_OFF = 78,
} NMDeviceStateReason;
/**

View file

@ -27,6 +27,24 @@ G_BEGIN_DECLS
#define NM_SETTING_HSR_PORT2 "port2"
#define NM_SETTING_HSR_MULTICAST_SPEC "multicast-spec"
#define NM_SETTING_HSR_PRP "prp"
#define NM_SETTING_HSR_PROTOCOL_VERSION "protocol-version"
#define NM_SETTING_HSR_INTERLINK "interlink"
/**
* NMSettingHsrProtocolVersion:
* @NM_SETTING_HSR_PROTOCOL_VERSION_DEFAULT: Default version for the protocol
* @NM_SETTING_HSR_PROTOCOL_VERSION_HSR_2010: HSRv0, IEC 62439-3:2010
* @NM_SETTING_HSR_PROTOCOL_VERSION_HSR_2012: HSRv1, IEC 62439-3:2012
*
* #NMSettingHsrProtocolVersion values indicate the HSR protocol version.
*
* Since: 1.56, 1.54.2
*/
typedef enum {
NM_SETTING_HSR_PROTOCOL_VERSION_DEFAULT = -1,
NM_SETTING_HSR_PROTOCOL_VERSION_HSR_2010 = 0,
NM_SETTING_HSR_PROTOCOL_VERSION_HSR_2012 = 1,
} NMSettingHsrProtocolVersion;
typedef struct _NMSettingHsrClass NMSettingHsrClass;
@ -43,6 +61,10 @@ NM_AVAILABLE_IN_1_46
guint32 nm_setting_hsr_get_multicast_spec(NMSettingHsr *setting);
NM_AVAILABLE_IN_1_46
gboolean nm_setting_hsr_get_prp(NMSettingHsr *setting);
NM_AVAILABLE_IN_1_54_2
NMSettingHsrProtocolVersion nm_setting_hsr_get_protocol_version(NMSettingHsr *setting);
NM_AVAILABLE_IN_1_54_2
const char *nm_setting_hsr_get_interlink(NMSettingHsr *setting);
G_END_DECLS

View file

@ -261,6 +261,9 @@ nm_utils_base64secret_decode(const char *base64_key, gsize required_key_len, gui
NM_AVAILABLE_IN_1_42
void nm_utils_ensure_gtypes(void);
NM_AVAILABLE_IN_1_54_3
char *nm_utils_copy_cert_as_user(const char *filename, const char *user, GError **error);
G_END_DECLS
#endif /* __NM_UTILS_H__ */

View file

@ -78,6 +78,8 @@
#define NM_VERSION_1_50 (NM_ENCODE_VERSION(1, 50, 0))
#define NM_VERSION_1_52 (NM_ENCODE_VERSION(1, 52, 0))
#define NM_VERSION_1_54 (NM_ENCODE_VERSION(1, 54, 0))
#define NM_VERSION_1_54_2 (NM_ENCODE_VERSION(1, 54, 2))
#define NM_VERSION_1_54_3 (NM_ENCODE_VERSION(1, 54, 3))
/* For releases, NM_API_VERSION is equal to NM_VERSION.
*
@ -89,7 +91,7 @@
#define NM_API_VERSION \
(((NM_MINOR_VERSION % 2) == 1) \
? NM_ENCODE_VERSION(NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0) \
: NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION , ((NM_MICRO_VERSION + 1) / 2) * 2))
: NM_ENCODE_VERSION(NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION + 1))
/* deprecated. */
#define NM_VERSION_CUR_STABLE NM_API_VERSION

View file

@ -439,6 +439,18 @@
#define NM_AVAILABLE_IN_1_54
#endif
#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_54_2
#define NM_AVAILABLE_IN_1_54_2 G_UNAVAILABLE(1, 54.2)
#else
#define NM_AVAILABLE_IN_1_54_2
#endif
#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_54_3
#define NM_AVAILABLE_IN_1_54_3 G_UNAVAILABLE(1, 54.3)
#else
#define NM_AVAILABLE_IN_1_54_3
#endif
/*
* Synchronous API for calling D-Bus in libnm is deprecated. See
* https://networkmanager.dev/docs/libnm/latest/usage.html#sync-api

View file

@ -64,6 +64,8 @@ NM_AVAILABLE_IN_1_4
gboolean nm_vpn_plugin_info_supports_hints(NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_42
gboolean nm_vpn_plugin_info_supports_multiple(NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_54_3
gboolean nm_vpn_plugin_info_supports_safe_private_file_access(NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_4
const char *const *nm_vpn_plugin_info_get_aliases(NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_2

View file

@ -253,7 +253,8 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1));
#define IFLA_HSR_SEQ_NR 5
#define IFLA_HSR_VERSION 6
#define IFLA_HSR_PROTOCOL 7
#define __IFLA_HSR_MAX 8
#define IFLA_HSR_INTERLINK 8
#define __IFLA_HSR_MAX 9
/*****************************************************************************/
@ -5196,6 +5197,15 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
NLA_PUT_U8(msg, IFLA_HSR_MULTICAST_SPEC, props->multicast_spec);
NLA_PUT_U8(msg, IFLA_HSR_PROTOCOL, props->prp);
if (!props->prp && props->protocol_version >= 0) {
NLA_PUT_U8(msg, IFLA_HSR_VERSION, props->protocol_version);
}
if (props->interlink > 0) {
NLA_PUT_U32(msg, IFLA_HSR_INTERLINK, props->interlink);
}
break;
}
case NM_LINK_TYPE_SIT:

View file

@ -6560,6 +6560,8 @@ nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len)
const char *
nm_platform_lnk_hsr_to_string(const NMPlatformLnkHsr *lnk, char *buf, gsize len)
{
char interlink_buf[30];
if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
return buf;
@ -6569,12 +6571,17 @@ nm_platform_lnk_hsr_to_string(const NMPlatformLnkHsr *lnk, char *buf, gsize len)
"port1 %d "
"port2 %d "
"supervision_address " NM_ETHER_ADDR_FORMAT_STR " multicast_spec %u "
"prp %s",
"prp %s "
"protocol_version %d"
"%s", /* interlink */
lnk->port1,
lnk->port2,
NM_ETHER_ADDR_FORMAT_VAL(&lnk->supervision_address),
lnk->multicast_spec,
lnk->prp ? "on" : "off");
lnk->prp ? "on" : "off",
lnk->protocol_version,
lnk->interlink ? nm_sprintf_buf(interlink_buf, " interlink %d", lnk->interlink)
: "");
return buf;
}
@ -8488,7 +8495,9 @@ nm_platform_lnk_hsr_hash_update(const NMPlatformLnkHsr *obj, NMHashState *h)
obj->port2,
obj->supervision_address,
obj->multicast_spec,
NM_HASH_COMBINE_BOOLS(guint8, obj->prp));
NM_HASH_COMBINE_BOOLS(guint8, obj->prp),
obj->protocol_version,
obj->interlink);
}
int
@ -8500,6 +8509,8 @@ nm_platform_lnk_hsr_cmp(const NMPlatformLnkHsr *a, const NMPlatformLnkHsr *b)
NM_CMP_FIELD_MEMCMP(a, b, supervision_address);
NM_CMP_FIELD(a, b, multicast_spec);
NM_CMP_FIELD_BOOL(a, b, prp);
NM_CMP_FIELD(a, b, protocol_version);
NM_CMP_FIELD(a, b, interlink);
return 0;
}

View file

@ -845,7 +845,9 @@ typedef struct {
typedef struct {
int port1;
int port2;
int interlink;
NMEtherAddr supervision_address;
gint8 protocol_version;
guint8 multicast_spec;
bool prp : 1;
} _nm_alignas(NMPlatformObject) NMPlatformLnkHsr;

View file

@ -4,10 +4,14 @@
#include "nm-std-utils.h"
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <net/if.h>
#include <pwd.h>
#include <stdint.h>
#include <sys/types.h>
/*****************************************************************************/
@ -95,6 +99,114 @@ out_huge:
/*****************************************************************************/
bool
nm_utils_set_effective_user(const char *user, char *errbuf, size_t errbuf_len)
{
struct passwd *pwentry;
int errsv;
char error[1024];
errno = 0;
pwentry = getpwnam(user);
if (!pwentry) {
errsv = errno;
if (errsv == 0) {
snprintf(errbuf, errbuf_len, "user not found");
} else {
snprintf(errbuf,
errbuf_len,
"error getting user entry: %d (%s)\n",
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
}
return false;
}
if (setgid(pwentry->pw_gid) != 0) {
errsv = errno;
snprintf(errbuf,
errbuf_len,
"failed to change group to %u: %d (%s)\n",
pwentry->pw_gid,
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
return false;
}
if (initgroups(user, pwentry->pw_gid) != 0) {
errsv = errno;
snprintf(errbuf,
errbuf_len,
"failed to reset supplementary group list to %u: %d (%s)\n",
pwentry->pw_gid,
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
return false;
}
if (setuid(pwentry->pw_uid) != 0) {
errsv = errno;
snprintf(errbuf,
errbuf_len,
"failed to change user to %u: %d (%s)\n",
pwentry->pw_uid,
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
return false;
}
return true;
}
/*****************************************************************************/
bool
nm_utils_read_file_to_stdout(const char *filename, char *errbuf, size_t errbuf_len)
{
nm_auto_close int fd = -1;
char buffer[4096];
char error[1024];
ssize_t bytes_read;
int errsv;
fd = open(filename, O_RDONLY);
if (fd == -1) {
errsv = errno;
snprintf(errbuf,
errbuf_len,
"error opening the file: %d (%s)",
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
return false;
}
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
if (fwrite(buffer, 1, bytes_read, stdout) != (size_t) bytes_read) {
errsv = errno;
snprintf(errbuf,
errbuf_len,
"error writing to stdout: %d (%s)",
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
return false;
}
}
if (bytes_read < 0) {
errsv = errno;
snprintf(errbuf,
errbuf_len,
"error reading the file: %d (%s)",
errsv,
_nm_strerror_r(errsv, error, sizeof(error)));
return false;
}
return true;
}
/*****************************************************************************/
/**
* _nm_strerror_r:
* @errsv: the errno passed to strerror_r()

View file

@ -37,4 +37,8 @@ size_t nm_utils_get_next_realloc_size(bool true_realloc, size_t requested);
const char *_nm_strerror_r(int errsv, char *buf, size_t buf_size);
bool nm_utils_set_effective_user(const char *user, char *errbuf, size_t errbuf_size);
bool nm_utils_read_file_to_stdout(const char *filename, char *errbuf, size_t errbuf_len);
#endif /* __NM_STD_UTILS_H__ */

View file

@ -467,7 +467,7 @@ NM_UTILS_LOOKUP_STR_DEFINE(
N_("The Wi-Fi P2P peer could not be found")),
NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED,
N_("The device handler dispatcher returned an error")),
NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING,
NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_MANAGER_DISABLED,
N_("The device is unmanaged because networking is disabled "
"or the system is suspended")),
NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING,
@ -492,6 +492,7 @@ NM_UTILS_LOOKUP_STR_DEFINE(
NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN,
N_("The device is unmanaged because it is an external device and is "
"unconfigured (down or without addresses)")),
NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_NETWORKING_OFF, N_("Networking was disabled")),
);

View file

@ -6198,6 +6198,12 @@ static const NMMetaPropertyInfo *const property_infos_HSR[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_HSR_PRP,
.property_type = &_pt_gobject_bool,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_HSR_PROTOCOL_VERSION,
.property_type = &_pt_gobject_enum,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_HSR_INTERLINK,
.property_type = &_pt_gobject_string,
),
NULL
};

View file

@ -116,7 +116,8 @@ typedef enum {
NM_META_COLOR_PERMISSION_UNKNOWN,
NM_META_COLOR_PERMISSION_YES,
NM_META_COLOR_PROMPT,
NM_META_COLOR_STATE_ASLEEP,
NM_META_COLOR_STATE_DISABLED,
NM_META_COLOR_STATE_ASLEEP = NM_META_COLOR_STATE_DISABLED, /* Deprecated */
NM_META_COLOR_STATE_CONNECTED_GLOBAL,
NM_META_COLOR_STATE_CONNECTED_LOCAL,
NM_META_COLOR_STATE_CONNECTED_SITE,

View file

@ -171,9 +171,11 @@
#define DESCRIBE_DOC_NM_SETTING_GSM_SIM_ID N_("The SIM card unique identifier (as given by the WWAN management service) which this connection applies to. If given, the connection will apply to any device also allowed by \"device-id\" which contains a SIM card matching the given identifier.")
#define DESCRIBE_DOC_NM_SETTING_GSM_SIM_OPERATOR_ID N_("A MCC/MNC string like \"310260\" or \"21601\" identifying the specific mobile network operator which this connection applies to. If given, the connection will apply to any device also allowed by \"device-id\" and \"sim-id\" which contains a SIM card provisioned by the given operator.")
#define DESCRIBE_DOC_NM_SETTING_GSM_USERNAME N_("The username used to authenticate with the network, if required. Many providers do not require a username, or accept any username. But if a username is required, it is specified here.")
#define DESCRIBE_DOC_NM_SETTING_HSR_INTERLINK N_("The optional interlink port name of the HSR interface.")
#define DESCRIBE_DOC_NM_SETTING_HSR_MULTICAST_SPEC N_("The last byte of supervision address.")
#define DESCRIBE_DOC_NM_SETTING_HSR_PORT1 N_("The port1 interface name of the HSR. This property is mandatory.")
#define DESCRIBE_DOC_NM_SETTING_HSR_PORT2 N_("The port2 interface name of the HSR. This property is mandatory.")
#define DESCRIBE_DOC_NM_SETTING_HSR_PROTOCOL_VERSION N_("Configures the protocol version to be used for the HSR/PRP interface. \"default\" (-1) sets the protocol version to the default version for the protocol. \"hsr-2010\" (0) sets the protocol version to HSRv0 (IEC 62439-3:2010). \"hsr-2012\" (1) sets the protocol version to HSRv1 (IEC 62439-3:2012).")
#define DESCRIBE_DOC_NM_SETTING_HSR_PRP N_("The protocol used by the interface, whether it is PRP or HSR.")
#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MAC_ADDRESS N_("If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")

View file

@ -103,8 +103,7 @@ if enable_nmtui
endif
subdir('nmcli')
subdir('nm-dispatcher')
subdir('nm-priv-helper')
subdir('nm-daemon-helper')
subdir('nm-helpers')
subdir('nm-online')
if enable_nmtui
subdir('nmtui')

View file

@ -1,11 +0,0 @@
nm-daemon-helper
================
A internal helper application that is spawned by NetworkManager
to perform certain actions.
Currently all it does is doing a reverse DNS lookup, which
cannot be done by NetworkManager because the operation requires
to reconfigure the libc resolver (which is a process-wide operation).
This is not directly useful to the user.

View file

@ -1,15 +0,0 @@
executable(
'nm-daemon-helper',
'nm-daemon-helper.c',
include_directories : [
src_inc,
top_inc,
],
link_with: [
libnm_std_aux,
],
link_args: ldflags_linker_script_binary,
link_depends: linker_script_binary,
install: true,
install_dir: nm_libexecdir,
)

View file

@ -1,5 +1,32 @@
nm-helpers
==========
This directory contains stand-alone helper programs used by various
components.
nm-daemon-helper
----------------
A internal helper application that is spawned by NetworkManager to
perform certain actions which can't be done in the daemon.
Currently it's used to do a reverse DNS lookup after reconfiguring the
libc resolver (which is a process-wide operation), and to read files
on behalf of unprivileged users (which requires a seteuid that affects
all the threads of the process).
This is not directly useful to the user.
nm-libnm-helper
---------------
A internal helper application that is spawned by libnm to perform
certain actions without impacting the calling process.
This is not directly useful to the user.
nm-priv-helper
==============
--------------
This is a D-Bus activatable, exit-on-idle service, which
provides an internal API to NetworkManager daemon.

View file

@ -1,5 +1,45 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# nm-daemon-helper
executable(
'nm-daemon-helper',
'nm-daemon-helper.c',
include_directories : [
src_inc,
top_inc,
],
link_with: [
libnm_std_aux,
],
link_args: ldflags_linker_script_binary,
link_depends: linker_script_binary,
install: true,
install_dir: nm_libexecdir,
)
# nm-libnm-helper
executable(
'nm-libnm-helper',
['nm-libnm-helper.c'],
include_directories : [
src_inc,
top_inc,
],
dependencies: [
glib_dep,
],
link_with: [
libnm_glib_aux,
libnm_std_aux,
],
install: true,
install_dir: nm_libexecdir,
)
# nm-priv-helper
configure_file(
input: 'org.freedesktop.nm_priv_helper.service.in',
output: '@BASENAME@',

View file

@ -137,6 +137,37 @@ cmd_resolve_address(void)
return RETURN_ERROR;
}
static int
cmd_read_file_as_user(void)
{
nm_auto_free char *user = NULL;
nm_auto_free char *filename = NULL;
char error[1024];
user = read_arg();
if (!user)
return RETURN_INVALID_ARGS;
filename = read_arg();
if (!filename)
return RETURN_INVALID_ARGS;
if (more_args())
return RETURN_INVALID_ARGS;
if (!nm_utils_set_effective_user(user, error, sizeof(error))) {
fprintf(stderr, "Failed to set effective user '%s': %s", user, error);
return RETURN_ERROR;
}
if (!nm_utils_read_file_to_stdout(filename, error, sizeof(error))) {
fprintf(stderr, "Failed to read file '%s' as user '%s': %s", filename, user, error);
return RETURN_ERROR;
}
return RETURN_SUCCESS;
}
int
main(int argc, char **argv)
{
@ -150,6 +181,8 @@ main(int argc, char **argv)
return cmd_version();
if (nm_streq(cmd, "resolve-address"))
return cmd_resolve_address();
if (nm_streq(cmd, "read-file-as-user"))
return cmd_read_file_as_user();
return RETURN_INVALID_CMD;
}

View file

@ -0,0 +1,45 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "libnm-std-aux/nm-default-std.h"
#include <stdio.h>
enum {
RETURN_SUCCESS = 0,
RETURN_INVALID_CMD = 1,
RETURN_INVALID_ARGS = 2,
RETURN_ERROR = 3,
};
static int
read_file_as_user(const char *filename, const char *user)
{
char error[1024];
if (!nm_utils_set_effective_user(user, error, sizeof(error))) {
fprintf(stderr, "Failed to set effective user '%s': %s", user, error);
return RETURN_ERROR;
}
if (!nm_utils_read_file_to_stdout(filename, error, sizeof(error))) {
fprintf(stderr, "Failed to read file '%s' as user '%s': %s", filename, user, error);
return RETURN_ERROR;
}
return RETURN_SUCCESS;
}
int
main(int argc, char **argv)
{
if (argc <= 1)
return RETURN_INVALID_CMD;
if (nm_streq(argv[1], "read-file-as-user")) {
if (argc != 4)
return RETURN_INVALID_ARGS;
return read_file_as_user(argv[2], argv[3]);
}
return RETURN_INVALID_CMD;
}

View file

@ -1349,6 +1349,67 @@ reader_parse_ethtool(Reader *reader, char *argument)
_LOGW(LOGD_CORE, "rd.ethtool: extra argument ignored");
}
static void
reader_parse_dhcp_client_id(Reader *reader, char *argument)
{
NMConnection *connection;
NMSettingIPConfig *s_ip4;
const char *interface;
gs_free char *client_id = NULL;
gs_free guint8 *buf = NULL;
gsize len = 0;
interface = get_word(&argument, ':');
if (!interface) {
_LOGW(LOGD_CORE, "rd.net.dhcp.client-id: missing interface");
return;
}
if (!argument || !*argument) {
_LOGW(LOGD_CORE, "rd.net.dhcp.client-id: missing client-id");
return;
}
if (argument[0] == '@') {
/* The client-id is a plain string but we still encode it as
* hex string. Otherwise, we could pass the string as-is, but we
* would need to handle special keywords like "mac", "perm-mac", etc.
*/
if (argument[1] != '\0') {
len = strlen(argument);
buf = (guint8 *) nm_memdup(argument, len + 1);
buf[0] = '\0';
}
} else {
/* Try to parse it as hex string */
buf = nm_utils_hexstr2bin_alloc(argument, FALSE, FALSE, "-", 0, &len);
}
if (buf) {
client_id = nm_utils_bin2hexstr_full(buf, len, ':', FALSE, NULL);
}
if (!client_id) {
_LOGW(LOGD_CORE,
"rd.net.dhcp.client-id: invalid client-id \"%s\". Must be hexadecimal bytes "
"separated by dashes (for example \"00-01-02-03-04-05-06\"), or '@' followed by a "
"string",
argument);
return;
}
if (len < 2) {
_LOGW(LOGD_CORE,
"rd.net.dhcp.client-id: invalid client-id \"%s\". Must be at least two bytes",
argument);
return;
}
connection = reader_get_connection(reader, interface, NULL, TRUE);
s_ip4 = nm_connection_get_setting_ip4_config(connection);
g_object_set(s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, client_id, NULL);
}
static void
_normalize_conn(gpointer key, gpointer value, gpointer user_data)
{
@ -1365,6 +1426,8 @@ _normalize_conn(gpointer key, gpointer value, gpointer user_data)
NULL,
NM_SETTING_IP_CONFIG_DHCP_TIMEOUT,
NULL,
NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,
NULL,
NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER,
NULL,
NM_SETTING_IP_CONFIG_DHCP_DSCP,
@ -1583,6 +1646,8 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
g_ptr_array_add(znets, g_strdup(argument));
} else if (nm_streq(tag, "rd.znet_ifname")) {
reader_parse_znet_ifname(reader, argument);
} else if (nm_streq(tag, "rd.net.dhcp.client-id")) {
reader_parse_dhcp_client_id(reader, argument);
} else if (g_ascii_strcasecmp(tag, "BOOTIF") == 0) {
nm_clear_g_free(&bootif_val);
bootif_val = g_strdup(argument);

View file

@ -94,6 +94,33 @@ find_conn_for_wired_mac(GPtrArray *a, const char *hwaddr)
return NULL;
}
static gboolean
hfi_is_dhcp(struct nbft_info_hfi *hfi, int family)
{
/* There are several flags that may indicate the HFI is set for DHCP
* per NVM Express® Boot Specification, Revision 1.3. As the HFI
* Transport Flags (HFITFLAGS) are not publicly exposed by the libnvme
* API, only the DHCP Override (DHCPO) flag and the IP Origin (IPORIG)
* value is available.
*
* The bit 03 of the HFI Transport Flags (HFITFLAGS) is about an advanced
* stateless mechanism - IPv6-SLAAC and IPv6-ND, stating that "the DHCP
* Override bit shall be cleared to 0, and the IP Origin field shall
* be cleared to 0". This nm-initrd-generator will ignore this flag,
* expecting an IP address to be provided by the usual HFI fields
* just like in a static adressing case.
*
* DHCP Override (DHCPO): "The HFI information was populated by
* consuming the DHCP on this interface."
*
* IP Origin (IPORIG): "If set to 3h (IpPrefixOriginDhcp), then the
* IP Address was acquired through DHCP, and the IP Address specified
* in this HFI should not be reused by the OS."
*/
return hfi->tcp_info.dhcp_override || hfi->tcp_info.ip_origin == 3 /* IpPrefixOriginDhcp */
|| is_valid_addr(family, hfi->tcp_info.dhcp_server_ipaddr);
}
static NMConnection *
create_wired_conn(struct nbft_info_hfi *hfi,
const char *conn_name,
@ -183,6 +210,7 @@ parse_hfi(GPtrArray *a, struct nbft_info_hfi *hfi, const char *table_name, char
return;
}
g_ptr_array_add(a, parent_connection);
nm_clear_g_free(&conn_name);
}
conn_name = format_conn_name(table_name, hfi, TRUE);
@ -208,7 +236,7 @@ parse_hfi(GPtrArray *a, struct nbft_info_hfi *hfi, const char *table_name, char
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
NULL);
if (is_valid_addr(AF_INET, hfi->tcp_info.dhcp_server_ipaddr)) {
if (hfi_is_dhcp(hfi, family)) {
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_AUTO,
@ -260,7 +288,7 @@ parse_hfi(GPtrArray *a, struct nbft_info_hfi *hfi, const char *table_name, char
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
NULL);
if (is_valid_addr(AF_INET6, hfi->tcp_info.dhcp_server_ipaddr)) {
if (hfi_is_dhcp(hfi, family)) {
g_object_set(s_ip6,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP6_CONFIG_METHOD_AUTO,

View file

@ -6,6 +6,10 @@ test_units = [
'test-cmdline-reader',
]
if enable_nbft
test_units += [ 'test-nbft-reader' ]
endif
foreach test_unit : test_units
exe = executable(
test_unit,

View file

@ -2786,6 +2786,149 @@ test_plain_equal_char(void)
/*****************************************************************************/
#define _dhcp_client_id_check_invalid(arg) \
G_STMT_START \
{ \
gs_unref_hashtable GHashTable *_connections2 = NULL; \
\
_connections2 = _parse_cons(NM_MAKE_STRV(arg)); \
g_test_assert_expected_messages(); \
g_assert_cmpint(g_hash_table_size(_connections2), ==, 0); \
} \
G_STMT_END
#define _dhcp_client_id_check_v(strv, exp_ifname, exp_client_id) \
G_STMT_START \
{ \
gs_unref_object NMConnection *_connection = NULL; \
NMSettingIPConfig *_s_ip4; \
\
_connection = _parse_con(strv, exp_ifname); \
\
g_test_assert_expected_messages(); \
\
g_assert(nm_connection_get_setting_connection(_connection)); \
g_assert(nm_connection_is_type(_connection, NM_SETTING_WIRED_SETTING_NAME)); \
g_assert(nm_connection_get_setting_ip4_config(_connection)); \
g_assert(nm_connection_get_setting_ip6_config(_connection)); \
_s_ip4 = nm_connection_get_setting_ip4_config(_connection); \
g_assert(NM_IS_SETTING_IP_CONFIG(_s_ip4)); \
\
g_assert_cmpstr(nm_setting_ip4_config_get_dhcp_client_id(NM_SETTING_IP4_CONFIG(_s_ip4)), \
==, \
(exp_client_id)); \
} \
G_STMT_END
#define _dhcp_client_id_check(arg, exp_ifname, exp_client_id) \
_dhcp_client_id_check_v(NM_MAKE_STRV("" arg ""), (exp_ifname), (exp_client_id))
#define DHCP_CLIENT_ID_INVALID_MSG(_id) \
"cmdline-reader: " \
"rd.net.dhcp.client-id: invalid client-id \"" _id "\". Must be hexadecimal bytes " \
"separated by dashes (for example \"00-01-02-03-04-05-06\"), or '@' followed by a string"
static void
test_rd_dhcp_client_id(void)
{
NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dhcp.client-id: missing interface");
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=");
NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dhcp.client-id: missing interface");
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=:");
NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dhcp.client-id: missing client-id");
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=eth0:");
NMTST_EXPECT_NM_WARN(DHCP_CLIENT_ID_INVALID_MSG("invalid"));
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=eth0:invalid");
NMTST_EXPECT_NM_WARN(DHCP_CLIENT_ID_INVALID_MSG("01:AA:BB:CC:DD:EE:FF"));
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=eth0:01:AA:BB:CC:DD:EE:FF");
NMTST_EXPECT_NM_WARN(DHCP_CLIENT_ID_INVALID_MSG("@"));
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=eth0:@");
NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dhcp.client-id: invalid client-id \"01\". Must be "
"at least two bytes");
_dhcp_client_id_check_invalid("rd.net.dhcp.client-id=eth0:01");
/* Client-id with hex string */
_dhcp_client_id_check("rd.net.dhcp.client-id=eth0:01-aa-BB-cc-dd-EE-ff",
"eth0",
"01:aa:bb:cc:dd:ee:ff");
/* Client-id with plain string */
_dhcp_client_id_check("rd.net.dhcp.client-id=eth0:@test.com",
"eth0",
"00:74:65:73:74:2e:63:6f:6d");
/* Minimal client-id, hex */
_dhcp_client_id_check("rd.net.dhcp.client-id=eth1:01-02", "eth1", "01:02");
/* Minimal client-id, string */
_dhcp_client_id_check("rd.net.dhcp.client-id=eth1:@1", "eth1", "00:31");
/* Long client-id */
_dhcp_client_id_check(
"rd.net.dhcp.client-id=enp1s0:"
"01-02-03-04-05-06-07-08-09-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-"
"25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40-41-42-43-44-45-46-47-48-"
"49-50-51-52-53-54-55-56-57-58-59-60-61-62-63-64-65-66-67-68-69-70-71-72",
"enp1s0",
"01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:"
"25:26:27:28:29:30:31:32:33:34:35:36:37:38:39:40:41:42:43:44:45:46:47:48:"
"49:50:51:52:53:54:55:56:57:58:59:60:61:62:63:64:65:66:67:68:69:70:71:72");
/* Test ordering: client-id before ip= */
_dhcp_client_id_check_v(
NM_MAKE_STRV("rd.net.dhcp.client-id=eth0:aa-bb-cc-dd-ee-ff", "ip=eth0:dhcp"),
"eth0",
"aa:bb:cc:dd:ee:ff");
/* Test ordering: client-id after ip= */
_dhcp_client_id_check_v(
NM_MAKE_STRV("ip=eth2:dhcp", "rd.net.dhcp.client-id=eth2:ba-da-cc-dd-ee-ff"),
"eth2",
"ba:da:cc:dd:ee:ff");
/* Duplicate option: last wins */
_dhcp_client_id_check_v(NM_MAKE_STRV("ip=eth3:dhcp",
"rd.net.dhcp.client-id=eth3:01-02",
"rd.net.dhcp.client-id=eth3:01-03"),
"eth3",
"01:03");
/* Multiple connections */
{
gs_unref_hashtable GHashTable *connections = NULL;
NMConnection *connection;
NMSettingIP4Config *s_ip4;
connections = _parse_cons(NM_MAKE_STRV("ip=eth0:dhcp",
"ip=eth1:dhcp",
"rd.net.dhcp.client-id=eth1:01-01-01",
"rd.net.dhcp.client-id=eth0:00-00-00"));
g_assert_nonnull(connections);
g_assert_cmpint(g_hash_table_size(connections), ==, 2);
connection = g_hash_table_lookup(connections, "eth0");
g_assert_nonnull(connection);
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting_ip4_config(connection);
g_assert_nonnull(s_ip4);
g_assert_cmpstr(nm_setting_ip4_config_get_dhcp_client_id(s_ip4), ==, "00:00:00");
connection = g_hash_table_lookup(connections, "eth1");
g_assert_nonnull(connection);
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting_ip4_config(connection);
g_assert_nonnull(s_ip4);
g_assert_cmpstr(nm_setting_ip4_config_get_dhcp_client_id(s_ip4), ==, "01:01:01");
}
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -2848,6 +2991,7 @@ main(int argc, char **argv)
g_test_add_func("/initrd/cmdline/rd_ethtool", test_rd_ethtool);
g_test_add_func("/initrd/cmdline/plain_equal_char", test_plain_equal_char);
g_test_add_func("/initrd/cmdline/global_dns", test_global_dns);
g_test_add_func("/initrd/cmdline/rd_dhcp_client_id", test_rd_dhcp_client_id);
return g_test_run();
}

View file

@ -0,0 +1,397 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2025 Red Hat, Inc.
*/
#include "src/core/nm-default-daemon.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-glib-aux/nm-test-utils.h"
#include "nm-initrd-generator/nm-initrd-generator.h"
#define TEST_INITRD_DIR NM_BUILD_SRCDIR "/src/nm-initrd-generator/tests"
/*****************************************************************************/
static NMConnection *
find_connection_for_mac(NMConnection **nbft_connections, const char *expected_mac, guint32 vlan_id)
{
NMConnection **c;
NMSettingWired *s_wired;
NMSettingVlan *s_vlan;
const char *mac_address;
for (c = nbft_connections; c && *c; c++) {
s_wired = nm_connection_get_setting_wired(*c);
g_assert(s_wired);
mac_address = nm_setting_wired_get_mac_address(s_wired);
g_assert(mac_address);
if (!nm_utils_hwaddr_matches(mac_address, -1, expected_mac, -1))
continue;
s_vlan = nm_connection_get_setting_vlan(*c);
if (vlan_id > 0) {
if (!s_vlan)
continue;
if (nm_setting_vlan_get_id(s_vlan) != vlan_id)
continue;
} else if (s_vlan)
continue;
return *c;
}
return NULL;
}
static void
verify_connection(NMConnection *c, const char *expected_mac, guint32 expected_vlan_id)
{
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingVlan *s_vlan;
const char *mac_address;
nmtst_assert_connection_verifies_without_normalization(c);
s_con = nm_connection_get_setting_connection(c);
g_assert(s_con);
g_assert(g_str_has_prefix(nm_setting_connection_get_id(s_con), "NBFT"));
g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, NULL);
g_assert(nm_setting_connection_get_autoconnect_priority(s_con)
== NMI_AUTOCONNECT_PRIORITY_FIRMWARE);
s_wired = nm_connection_get_setting_wired(c);
g_assert(s_wired);
mac_address = nm_setting_wired_get_mac_address(s_wired);
g_assert(mac_address);
g_assert(nm_utils_hwaddr_matches(mac_address, -1, expected_mac, -1));
if (expected_vlan_id > 0) {
g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
==,
NM_SETTING_VLAN_SETTING_NAME);
s_vlan = nm_connection_get_setting_vlan(c);
g_assert(s_vlan);
g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, expected_vlan_id);
g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, NULL);
} else {
g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
==,
NM_SETTING_WIRED_SETTING_NAME);
}
}
static void
verify_ipv4(NMConnection *c, const char *addr, int mask, const char *gateway)
{
NMSettingIPConfig *s_ip4;
NMIPAddress *ip4_addr;
s_ip4 = nm_connection_get_setting_ip4_config(c);
g_assert(s_ip4);
g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
==,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip4), ==, 0);
g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip4), ==, 1);
ip4_addr = nm_setting_ip_config_get_address(s_ip4, 0);
g_assert(ip4_addr);
g_assert_cmpstr(nm_ip_address_get_address(ip4_addr), ==, addr);
g_assert_cmpint(nm_ip_address_get_prefix(ip4_addr), ==, mask);
g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip4), ==, gateway);
}
static void
verify_ipv4_dhcp(NMConnection *c)
{
NMSettingIPConfig *s_ip4;
s_ip4 = nm_connection_get_setting_ip4_config(c);
g_assert(s_ip4);
g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
}
static void
verify_ipv4_disabled(NMConnection *c)
{
NMSettingIPConfig *s_ip4;
s_ip4 = nm_connection_get_setting_ip4_config(c);
g_assert(s_ip4);
g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
==,
NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
}
static void
verify_ipv6(NMConnection *c, const char *addr, int prefix, const char *gateway)
{
NMSettingIPConfig *s_ip6;
NMIPAddress *ip6_addr;
s_ip6 = nm_connection_get_setting_ip6_config(c);
g_assert(s_ip6);
g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
==,
NM_SETTING_IP6_CONFIG_METHOD_MANUAL);
g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 0);
g_assert_cmpint(nm_setting_ip_config_get_num_addresses(s_ip6), ==, 1);
ip6_addr = nm_setting_ip_config_get_address(s_ip6, 0);
g_assert(ip6_addr);
g_assert_cmpstr(nm_ip_address_get_address(ip6_addr), ==, addr);
g_assert_cmpint(nm_ip_address_get_prefix(ip6_addr), ==, prefix);
g_assert_cmpstr(nm_setting_ip_config_get_gateway(s_ip6), ==, gateway);
}
static void
verify_ipv6_auto(NMConnection *c)
{
NMSettingIPConfig *s_ip6;
s_ip6 = nm_connection_get_setting_ip6_config(c);
g_assert(s_ip6);
g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
}
static void
verify_ipv6_disabled(NMConnection *c)
{
NMSettingIPConfig *s_ip6;
s_ip6 = nm_connection_get_setting_ip6_config(c);
g_assert(s_ip6);
g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
==,
NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
}
static int
count_nm_conn(NMConnection **connections)
{
int cnt;
for (cnt = 0; connections && *connections; connections++, cnt++)
;
return cnt;
}
static void
free_connections(NMConnection **connections)
{
NMConnection **c;
for (c = connections; c && *c; c++)
g_object_unref(*c);
g_free(connections);
}
static void
test_read_nbft_ipv4_static(void)
{
NMConnection **nbft_connections;
NMConnection *connection;
const char *expected_mac_address;
gs_free char *hostname = NULL;
nbft_connections = nmi_nbft_reader_parse(TEST_INITRD_DIR "/nbft-ipv4-static", &hostname);
g_assert_nonnull(hostname);
g_assert_cmpint(count_nm_conn(nbft_connections), ==, 6);
/* NBFT-multi HFI 1 */
expected_mac_address = "52:54:00:72:c5:ae";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4(connection, "192.168.122.158", 24, "192.168.122.1");
verify_ipv6_disabled(connection);
/* NBFT-multi HFI 2 */
expected_mac_address = "52:54:00:72:c5:af";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4_dhcp(connection);
verify_ipv6_disabled(connection);
/* NBFT-Dell.PowerEdge.R660-fw1.5.5-mpath+discovery HFI 1 */
expected_mac_address = "00:62:0b:cb:eb:70";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4(connection, "172.18.240.1", 24, NULL);
verify_ipv6_disabled(connection);
/* NBFT-Dell.PowerEdge.R660-fw1.5.5-mpath+discovery HFI 2 */
expected_mac_address = "00:62:0b:cb:eb:71";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4(connection, "172.18.230.2", 24, NULL);
verify_ipv6_disabled(connection);
/* NBFT-rhpoc */
expected_mac_address = "ea:eb:d3:58:89:58";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4(connection, "192.168.101.30", 24, NULL);
verify_ipv6_disabled(connection);
/* NBFT-static-ipv4 */
expected_mac_address = "52:54:00:b8:19:b9";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4(connection, "192.168.49.50", 24, NULL);
verify_ipv6_disabled(connection);
free_connections(nbft_connections);
}
static void
test_read_nbft_ipv4_dhcp(void)
{
NMConnection **nbft_connections;
NMConnection *connection;
const char *expected_mac_address;
gs_free char *hostname = NULL;
nbft_connections = nmi_nbft_reader_parse(TEST_INITRD_DIR "/nbft-ipv4-dhcp", &hostname);
g_assert_nonnull(hostname);
g_assert_cmpint(count_nm_conn(nbft_connections), ==, 2);
/* NBFT-dhcp-ipv4 */
expected_mac_address = "52:54:00:b8:19:b9";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4_dhcp(connection);
verify_ipv6_disabled(connection);
/* NBFT-Dell.PowerEdge.R760 */
expected_mac_address = "b0:26:28:e8:7c:0e";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4_dhcp(connection);
verify_ipv6_disabled(connection);
free_connections(nbft_connections);
}
static void
test_read_nbft_ipv6_static(void)
{
NMConnection **nbft_connections;
NMConnection *connection;
const char *expected_mac_address;
gs_free char *hostname = NULL;
nbft_connections = nmi_nbft_reader_parse(TEST_INITRD_DIR "/nbft-ipv6-static", &hostname);
g_assert_nonnull(hostname);
g_assert_cmpint(count_nm_conn(nbft_connections), ==, 1);
/* NBFT-static-ipv6 */
expected_mac_address = "52:54:00:9e:20:1a";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv6(connection, "fd09:9a46:b5c1:1fe::10", 64, NULL);
verify_ipv4_disabled(connection);
free_connections(nbft_connections);
}
static void
test_read_nbft_ipv6_auto(void)
{
NMConnection **nbft_connections;
NMConnection *connection;
const char *expected_mac_address;
gs_free char *hostname = NULL;
nbft_connections = nmi_nbft_reader_parse(TEST_INITRD_DIR "/nbft-ipv6-auto", &hostname);
g_assert_nonnull(hostname);
g_assert_cmpint(count_nm_conn(nbft_connections), ==, 3);
/* NBFT-auto-ipv6 */
expected_mac_address = "52:54:00:9e:20:1a";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv6(connection, "fd09:9a46:b5c1:1ff:5054:ff:fe9e:201a", 64, NULL);
verify_ipv4_disabled(connection);
/* NBFT-dhcp-ipv6 */
expected_mac_address = "52:54:00:b8:19:b9";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv6_auto(connection);
verify_ipv4_disabled(connection);
/* NBFT-ipv6-noip+disc */
expected_mac_address = "40:a6:b7:c0:8a:c9";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv6_auto(connection);
verify_ipv4_disabled(connection);
free_connections(nbft_connections);
}
static void
test_read_nbft_vlan(void)
{
NMConnection **nbft_connections;
NMConnection *connection;
const char *expected_mac_address;
gs_free char *hostname = NULL;
nbft_connections = nmi_nbft_reader_parse(TEST_INITRD_DIR "/nbft-vlan", &hostname);
g_assert_cmpstr(hostname, ==, NULL);
g_assert_cmpint(count_nm_conn(nbft_connections), ==, 4);
/* NBFT-qemu-vlans-incomplete HFI 1 */
expected_mac_address = "52:54:00:72:c5:ae";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4(connection, "192.168.122.158", 24, "192.168.122.1");
verify_ipv6_disabled(connection);
/* NBFT-qemu-vlans-incomplete HFI 2 */
expected_mac_address = "52:54:00:72:c5:af";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 0);
verify_connection(connection, expected_mac_address, 0);
verify_ipv4_disabled(connection);
verify_ipv6_disabled(connection);
/* NBFT-qemu-vlans-incomplete HFI 2 VLAN 11 */
expected_mac_address = "52:54:00:72:c5:af";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 11);
verify_connection(connection, expected_mac_address, 11);
verify_ipv4(connection, "192.168.124.58", 24, NULL);
verify_ipv6_disabled(connection);
/* NBFT-qemu-vlans-incomplete HFI 2 VLAN 12 */
expected_mac_address = "52:54:00:72:c5:af";
connection = find_connection_for_mac(nbft_connections, expected_mac_address, 12);
verify_connection(connection, expected_mac_address, 12);
verify_ipv4(connection, "192.168.125.58", 24, NULL);
verify_ipv6_disabled(connection);
free_connections(nbft_connections);
}
NMTST_DEFINE();
int
main(int argc, char **argv)
{
nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT");
g_test_add_func("/initrd/nbft/ipv4-static", test_read_nbft_ipv4_static);
g_test_add_func("/initrd/nbft/ipv4-dhcp", test_read_nbft_ipv4_dhcp);
g_test_add_func("/initrd/nbft/ipv6-static", test_read_nbft_ipv6_static);
g_test_add_func("/initrd/nbft/ipv6-auto", test_read_nbft_ipv6_auto);
g_test_add_func("/initrd/nbft/vlan", test_read_nbft_vlan);
return g_test_run();
}

View file

@ -1235,6 +1235,13 @@
nmcli-description="The protocol used by the interface, whether it is PRP or HSR."
format="boolean"
values="true/yes/on, false/no/off" />
<property name="protocol-version"
nmcli-description="Configures the protocol version to be used for the HSR/PRP interface. &quot;default&quot; (-1) sets the protocol version to the default version for the protocol. &quot;hsr-2010&quot; (0) sets the protocol version to HSRv0 (IEC 62439-3:2010). &quot;hsr-2012&quot; (1) sets the protocol version to HSRv1 (IEC 62439-3:2012)."
format="choice (NMSettingHsrProtocolVersion)"
values="default (-1), hsr-2010 (0), hsr-2012 (1)" />
<property name="interlink"
nmcli-description="The optional interlink port name of the HSR interface."
format="string" />
</setting>
<setting name="infiniband" >
<property name="mac-address"

View file

@ -28,7 +28,7 @@ static void permission_changed(GObject *gobject, GParamSpec *pspec, NmCli *nmc);
static NM_UTILS_LOOKUP_STR_DEFINE(nm_state_to_string,
NMState,
NM_UTILS_LOOKUP_DEFAULT(N_("unknown")),
NM_UTILS_LOOKUP_ITEM(NM_STATE_ASLEEP, N_("asleep")),
NM_UTILS_LOOKUP_ITEM(NM_STATE_DISABLED, N_("network off")),
NM_UTILS_LOOKUP_ITEM(NM_STATE_CONNECTING, N_("connecting")),
NM_UTILS_LOOKUP_ITEM(NM_STATE_CONNECTED_LOCAL,
N_("connected (local only)")),
@ -53,8 +53,8 @@ state_to_color(NMState state)
return NM_META_COLOR_STATE_CONNECTED_GLOBAL;
case NM_STATE_DISCONNECTING:
return NM_META_COLOR_STATE_DISCONNECTING;
case NM_STATE_ASLEEP:
return NM_META_COLOR_STATE_ASLEEP;
case NM_STATE_DISABLED:
return NM_META_COLOR_STATE_DISABLED;
case NM_STATE_DISCONNECTED:
return NM_META_COLOR_STATE_DISCONNECTED;
default:

Some files were not shown because too many files have changed in this diff Show more