Compare commits

...

240 commits
1.56.0 ... main

Author SHA1 Message Date
Rahul Rajesh
22f4e74942 merge: branch 'rr/fix-avc-error'
core: delay D-Bus type initialization for --print-config

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2376
2026-03-10 12:36:29 +00:00
Till Maas
57bc396c12 core: delay D-Bus type initialization for --print-config
The --print-config option should only read and print the configuration
without initializing any D-Bus infrastructure. However, g_type_ensure()
calls for D-Bus types were happening before the --print-config check,
causing GLib/GIO to set up D-Bus infrastructure and create cache
directories (~/.cache/bus or /root/.cache) unnecessarily.

Move the g_type_ensure() calls to after the --print-config (and
--version) early exits, so they only run when NetworkManager actually
needs to start normally and use D-Bus.

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

Assisted-by: Claude Code claude-sonnet-4-5@20250929
Signed-off-by: Till Maas <opensource@till.name>
2026-03-06 14:30:23 -05:00
Íñigo Huguet
0e2b679afb merge: branch 'ih/perm_unmanaged'
add API to manage/unmanage devices in a persistent way

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2358
2026-03-06 10:23:58 +00:00
Íñigo Huguet
b6bd9cee87 NEWS: update 2026-03-06 11:21:58 +01:00
Íñigo Huguet
2fbaca1cbc checkpoint: rollback devices' "permanently managed" configuration
If a device's "managed" configuration is changed persistently (stored to
NM-intern), it needs to be undone in a rollback.
2026-03-06 11:21:57 +01:00
Íñigo Huguet
1252f8dc7e core: config: add unit tests for the new get/set_device_managed 2026-03-06 11:21:56 +01:00
Íñigo Huguet
7ee50b687a nmcli: wait for device set async operation to finish
We need to wait for it to finish so we can show error messages, if any.

Also, if we don't do it, sometimes the `d set eth0 managed ...`
operation fails with the following message in the daemon's log: "Unable
to determine UID of the request". This is because the client's process
is terminated before the daemon can check the permissions, as it needs
to check the uid and gid from the client's process.
2026-03-06 11:21:55 +01:00
Íñigo Huguet
d2f98a1669 nmcli: add managed --permanent yes/no/up/down/reset
Allow to manage or unmanage a device persisting across reboots.
If --permanent is not specified, only the runtime managed state is
changed, preserving the previous behavior. The --permanent-only
option allows to edit only the persistent value, without touching
the runtime value.

Also add the values up/down. Up means managed=yes and set device's
administrative state UP. Down means managed=no and admin state DOWN.

Add the value 'reset' too. It reverts managed runtime status to default
behaviour. When used with `--permanent` flag, the persisted managed
settings is cleared.

Co-authored-by: Rahul Rajesh <rajeshrah22@gmail.com>
2026-03-06 11:21:53 +01:00
Íñigo Huguet
7c8f343f2c core: device: autoselect device match criteria in SetManaged()
Devices like veth without a permanent MAC address cannot be matched by
MAC. If using the BY_MAC flag in SetManaged(), the changes are not
effective for such kind of devices.

Add a BY_NAME flag, in addition to the BY_MAC one. If the client sets
one of them, it means to force this mode of matching. If none is
selected, the daemon will choose how to match, preferring matching by
MAC when possible, and by ifname when not possible.
2026-03-06 11:21:52 +01:00
Íñigo Huguet
b9725dab73 core: device: allow to change the admin state of the device in SetManaged()
Control it with a new NM_DEVICE_MANAGED_SET_ADMIN_STATE flag.
This flag will make that, at the same time that the device is moved to
managed/unmanaged, it's admin state is set to up/down. Many users want
to have a way to have their devices in a DOWN admin state when they are
not using them. Because of the complex activation process, NM wants to
have its devices in UP state all the time. However, it is not a problem
to have it DOWN if we are not managing it.
2026-03-06 11:21:51 +01:00
Íñigo Huguet
f346fcf977 core: device: allow to reset the managed property
Previous commits added the capability to persist to disk the value of
'managed' received via the D-Bus API. Users might need to clear the
previous content, thus reseting it to its default.

Although this is specially useful for the PERMANENT flag, we need to be
consistent and reset the runtime state too.
2026-03-06 11:21:50 +01:00
Íñigo Huguet
ec1522fa8c core: device: implement storing to disk for Device.SetManaged()
If the NM_DEVICE_MANAGED_FLAGS_PERMANENT flag is used, the value will be
stored to disk, to the NetworkManager-intern.conf file, in a [device-*]
section.

To modify the runtime value, the NM_DEVICE_MANAGED_FLAGS_RUNTIME must be
passed. This allows to control independently whether to modify only one
or both.
2026-03-06 11:21:48 +01:00
Íñigo Huguet
0a1503f052 core: config: allow to store 'managed' configs to NM-intern
To support setting devices as managed or unmanaged via D-Bus API in a
permanent way, we need a way to store this configuration on disk. Before
this commit, only config files manually edited allowed it. Following
commits will make use of the new functions to store [device-*] sections
into NetworkManager-intern.conf depending on D-Bus method invocations.
2026-03-06 11:21:48 +01:00
Íñigo Huguet
47c1b04f9e core: config-data: don't ignore [.intern.device/connection] sections
Now it is possible to have [.intern.device-*] sections in
NetworkManager-intern.conf. Take them into account when parsing the
configuration keyfiles.
2026-03-06 11:21:47 +01:00
Íñigo Huguet
9ff530c322 dbus: device: add SetManaged method
The 'Managed' property only sets the managed state in runtime, but it is
not possible to persist it to disk. Add a SetManaged method that will be
able to persist it to disk. In this commit, it just modify the runtime
state, so it actually only does the same than setting the property.
Storing to disk will be added in next commits.
2026-03-06 11:21:47 +01:00
Íñigo Huguet
121034d655 merge: branch 'ih/nmcli_field_ports'
nmcli: add BRIDGE.PORTS, TEAM.PORTS and GENERAL.CONTROLLER-PATH fields. Replace SLAVE for PORT in `c show`

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2369
2026-03-05 07:08:54 +00:00
Íñigo Huguet
cdd38f7cdf nmcli: replace SLAVE for PORT, still accepting SLAVE as alias
With `nmcli -f SLAVE` the PORT column will be shown. In this case we
don't duplicate the field because it's typically shown in columns and
having duplicated columns is more annoying than a duplicated row.
2026-03-05 07:08:14 +00:00
Íñigo Huguet
c6b6c7164b libnmc: allow to define an alias to match fields to show 2026-03-05 07:08:14 +00:00
Íñigo Huguet
ff1d435096 nmcli: add BRIDGE.PORTS, TEAM.PORTS and GENERAL.CONTROLLER-PATH fields
They show the same than the old BRIDGE/TEAM.SLAVES and GENERAL.MASTER-PATH.
We missed this when we did the changes in favour of conscious language.
Instead of replacing them, we add a new field that will show the same
value with the new name. This way we avoid breaking users doing
`nmcli -f BRIDGE.SLAVES` or `nmcli ... | grep SLAVES`.
2026-03-05 07:08:14 +00:00
Beniamino Galvani
1a2fec62f0 merge: branch 'bg/drop-veth-peer-ioctl'
platform: drop ioctl fallback for finding veth's peer

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2354
2026-03-04 17:27:08 +00:00
Beniamino Galvani
fddda02825 platform: drop ioctl fallback for finding veth's peer
The peer ifindex of a veth interface is available via netlink since
kernel 4.1 released in 2015. Drop the code that falls back to ioctl.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1881
2026-03-04 16:51:57 +00:00
Beniamino Galvani
2d30b71dd4 merge: branch 'bg/dhcp-routes'
dhcp: ignore the Router option when there are Classless Static Routes

Closes #834

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2364
2026-03-04 16:51:33 +00:00
Beniamino Galvani
7651ef0386 dhcp: ignore the Router option when there are Classless Static Routes
RFC 3442 says:

   If the DHCP server returns both a Classless Static Routes option and
   a Router option, the DHCP client MUST ignore the Router option.

Currently the internal client is ignoring the Router option only if
the Classless Static Routes option doesn't include a default route,
which is different from what is recommended in the RFC. Fix the behavior.

Fixes: 6adade6f21 ('dhcp: add nettools dhcp4 client')

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/834
2026-03-04 15:26:11 +00:00
Íñigo Huguet
8236c1c16a merge: branch 'lr/mtu-clarity'
device: do not set MTU twice in stage3

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2231
2026-03-04 11:15:19 +00:00
Lubomir Rintel
818cf77cb5 device: do not set MTU twice in stage3
The pair of _commit_mtu() calls in activate_stage3_ip_config() are very
heavily commented, but it is still not clear why would there be two of
them.

Remove one, and try to clarify the situation in an updated comment.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2231
2026-03-04 11:15:01 +00:00
Jan Vaclav
98256be220 merge: branch 'jv/onlink'
platform: introduce per-nexthop onlink attribute

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2368
2026-03-03 10:10:49 +00:00
Jan Vaclav
d564a0c3f9 platform: track onlink flag per-nexthop for IPv4 routes
In kernel, the onlink flag (RTNH_F_ONLINK) is associated with each
nexthop (rtnh_flags) rather than the route as a whole. NM previously
stored it only per-route in NMPlatformIPRoute.r_rtm_flags, which meant
that two nexthops only differing with the onlink flag were combined
as one entry in the platform cache.

Fix this by tracking the onlink flag per-nexthop.

Resolves: https://issues.redhat.com/browse/NMT-1486
2026-03-02 10:57:56 +00:00
Beniamino Galvani
a924826e2c merge: branch 'rr/fix-create-and-realize'
device: fix vxlan create_and_realize

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2363
2026-02-27 16:33:42 +00:00
Rahul Rajesh
04f7ca5029 device: fix vxlan create_and_realize
Assert that remote and local will be valid in this function since it
will be verified in verify function in connection profile.
2026-02-27 16:26:16 +00:00
Beniamino Galvani
de6d9930b9 gitlab: improve the merge request template
Mention the commit subject format.
2026-02-27 11:59:16 +01:00
Beniamino Galvani
30ca65ab88 merge: branch 'kk-l10n-add'
Add Kazakh translation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2359
2026-02-27 09:41:00 +00:00
Baurzhan Muftakhidinov
bf80a9019e Add Kazakh translation 2026-02-27 10:02:13 +01:00
Beniamino Galvani
6b37d612fd merge: branch 'nmtui-tilde'
nmtui: allow enter tilde in search domains

Closes #1862

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2356
2026-02-27 08:58:02 +00:00
Vladislav Tsisyk
dbeb7fa0f6 nmtui: accept tilde in search domains
Closes #1862
2026-02-27 08:57:20 +00:00
Beniamino Galvani
b7d9625ae1 merge: branch 'lr/systemd-unit-lockdown'
data/NetworkManager.service: restrict the unit some more

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2062
2026-02-27 08:46:04 +00:00
Lubomir Rintel
cb51c4475a data/NetworkManager.service: restrict the unit some more
This adds some low-hanging food to improve our score with "systemd-analyze
security" by one point:

Before:
  → Overall exposure level for NetworkManager.service: 7.8 EXPOSED 🙁

After:
  → Overall exposure level for NetworkManager.service: 6.8 MEDIUM 😐

Nothing particularly impactful here: we still got DAC_OVERRIDE, we still
can insert loadable modules (as opposed to relying on autoload) and
read user home directories. But there's a slight chance this may save
our butts one day, who knows.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2062
2026-02-27 08:44:10 +00:00
Beniamino Galvani
756e612858 merge: branch 'fix-vpn-search-domains'
vpn: set search domains

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2340
2026-02-26 17:00:08 +00:00
François HORTA
03a3a449f6 vpn: set search domains
dns-search parameters set on VPN connections should be merged with
domains received through the VPN (which may be empty if the connection
sets ignore-auto-dns).

This is currently not the case because domains received by the VPN
connection are only added through nm_l3_config_data_add_domain.

If dns-search is unset, this behaves correctly because the structure
built in _mgr_configs_data_construct in src/core/dns/nm-dns-manager.c
correctly uses the domains from nm_l3_config_data_get_domains.

However if dns-search is set, nm_l3_config_data_get_searches is no
longer empty and it takes precedence because of the "n_searches > 0"
condition.
2026-02-26 16:40:59 +00:00
Beniamino Galvani
926795f11a merge: branch 'bulgarian'
Updated bulgarian translation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2350
2026-02-26 15:12:51 +00:00
twlvnn
e4ca177be2 Updated bulgarian translation 2026-02-26 15:01:49 +00:00
Beniamino Galvani
20f975abe3 merge: branch 'bg/secret-permissions'
Fix the handling of permissions for connection secrets

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2367
2026-02-25 08:24:10 +00:00
Beniamino Galvani
024360bffa settings: fix check on existing system secrets
The previous check was based only on the presence of a non-NULL
"existing_secrets" GVariant. That GVariant is created via:

  nm_connection_to_dbus(nm_settings_connection_get_connection(self),
                        NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED)

The function returns a GVariant containing a first-level dictionary
for each setting, even for those that doesn't contain any secrets. As
a result, the check was requiring the system.modify permission even if
there weren't any cached secrets to send to the agent.

Fix the check to actually check for the presence of any secrets in the
cached dictionary. Some connection types have a third-level
dictionary that can be empty, for example VPNs have vpn.secrets.
2026-02-25 09:05:04 +01:00
Beniamino Galvani
db0825a110 settings: accept not-saved secrets from agents without modify-system
The "modify.system" polkit permission allows a user to modify settings
for connection profiles that belong to all users.

For this reason, when an agent returns system secrets (i.e. secrets
that are going to be stored to disk), NetworkManager checks that the
agent has the modify.system permission.

If a secret has the AGENT_OWNED flag, it's stored in the agent
itself. If the secret has the NOT_SAVED flag, it will be asked to
users at the beginning of every connection attempt.

In both those cases the profile is not modified and there is no need
for the modify.system permission. Fix the check to also consider the
NOT_SAVED flag.
2026-02-24 08:46:32 +01:00
Beniamino Galvani
eff8330b57 libnm-core: add missing flags check in .to_dbus_function()
Properties that define a .to_dbus_function() as a D-Bus override, need
to return early if the flags only ask to serialize secrets.

Fixes: 7fb23b0a62 ('libnm: add NMIPRoutingRule API')
2026-02-24 08:46:32 +01:00
Beniamino Galvani
128b49fe21 merge: branch 'rr/geneve-support'
geneve: add support for GENEVE tunnels

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2352
2026-02-18 12:43:47 +00:00
Rahul Rajesh
2e2b4946ea NEWS: add support for GENEVE interface
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2352

Resolves: https://issues.redhat.com/browse/RHEL-122042
2026-02-17 16:02:45 -05:00
Rahul Rajesh
0bfb8fa89d geneve: added GENEVE device support
Support device type geneve in libnm and nmcli.
2026-02-17 15:21:03 -05:00
Rahul Rajesh
2aaf88375e geneve: add connection profile settings
Added support for the following properties in connection profile:
id (VNI), remote IPv4/IPv6, ttl, tos, df, destination port.

See IP-LINK(8) manual page with command `man 8 ip-link` for more details
on the properties. See also previous commit for nm supported attributes.

id and remote are mandatory attributes:
```
$ nmcli connection add type geneve save no
Error: 'id' argument is required.
$ nmcli connection add type geneve id 42 save no
Error: 'remote' argument is required.
```
2026-02-17 15:21:03 -05:00
Rahul Rajesh
29c8bbe21a platform: add support for GENEVE tunnels
GENEVE (Generic Network Virtualization Encapsulation) is a network
tunneling protocol that provides a flexible encapsulation format for
overlay networks. It uses UDP as the transport protocol and supports
variable-length metadata in the tunnel header.

This patch adds GENEVE tunnel to NM's platform layer:

- Add platform API functions (nm_platform_link_geneve_add,
  nm_platform_link_get_lnk_geneve)

- Netlink message parsing for the following attributes:
  * IFLA_GENEVE_ID - VNI (Virtual Network Identifier)
  IPv4 and IPv6 remote
  * IFLA_GENEVE_REMOTE
  * IFLA_GENEVE_REMOTE6
  TTL, TOS, and DF flags
  * IFLA_GENEVE_TTL
  * IFLA_GENEVE_TOS
  * IFLA_GENEVE_DF
  UDP destination port
  * IFLA_GENEVE_PORT

- Add test cases for GENEVE tunnel creation and detection with two test
  modes covering IPv4 and IPv6.

The implementation tries to follow the same patterns as other tunnel
types (GRE, VXLAN, etc.) and integrates with the existing platform
abstraction layer.
2026-02-17 15:21:03 -05:00
Rahul Rajesh
ad78bd8570 platform: expand nmp object type flags to guint64
To allow for more than 32 NMP_OBJECT_* types.
2026-02-17 15:21:03 -05:00
Beniamino Galvani
54a4b42c05 merge: branch 'docs/get_secrets_fix'
docs: GetSecrets doesn't accept empty string

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2355
2026-02-17 10:35:30 +00:00
Mattia Dal Ben
8c93d0bdff introspection: fix documentation for GetSecrets 2026-02-17 09:01:20 +00:00
Beniamino Galvani
4fcebeaec0 merge: branch 'man-nmcli-checkpoint'
man: fix sentence in nmcli manual page

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2351
2026-02-17 09:00:40 +00:00
Federico Ton
40f19ad674 man: fix sentence in nmcli manual page
A not very clear sentence in the description of the `nmcli device checkpoint` command has been changed.
2026-02-16 18:57:52 +01:00
Vladimír Beneš
f70b37357a release: bump version to 1.57.3 (development) 2026-02-13 13:30:02 +01:00
Vladimír Beneš
30a5416a54 tmp 2026-02-13 13:23:55 +01:00
Beniamino Galvani
b5410bb24a merge: branch 'bg/clat-improvements'
CLAT improvements

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2353
2026-02-10 08:53:35 +00:00
Beniamino Galvani
a4e30ee849 clat: print translation statistics during deactivation
Print some statistics about the translation when the connection goes
down:

  clat: stats: egress (v4 to v6): tcp 1275, udp 191, icmp 9, other 0, dropped 2; ingress (v6 to v4): tcp 1669, udp 272, icmp 0, other 0, fragment 136, dropped 0

Those counters can be used to better understand what's going wrong in
case of problems; for example, if the packets are being dropped in the
ingress path or in the egress one.
2026-02-06 17:47:33 +01:00
Beniamino Galvani
112190d09a clat: support layer3 interfaces
When running the CLAT over an interface that doesn't use the Ethernet
header, like an IP tunnel, there are some changes needed. The BPF
program must compute offsets differently. Also, the DAD packet should
not include an Ethernet header.
2026-02-06 17:47:30 +01:00
Beniamino Galvani
d7edc806b6 core: clat: add the "nm" prefix to ebpf program names
The program names are displayed in the "bpftool prog" output. It is
easier to recognize NM programs if they have the "nm" prefix.
2026-02-06 10:38:07 +01:00
Beniamino Galvani
f9b2083394 l3cd: rename "clat" to "clat_config"
The member indicates if CLAT is enabled in the configuration. Use a
clearer name.
2026-02-06 10:38:06 +01:00
Beniamino Galvani
e2cdd5c4dc build: don't require libndp >= 1.9 if CLAT is disabled
libndp >= 1.9 is only required to parse the PREF64 option needed for
CLAT. When building NM in an enviroment with an older libndp, still
allow building without CLAT support.
2026-02-06 10:38:05 +01:00
Beniamino Galvani
c86d234516 nmcli: show the CLAT state
It is useful to show that the CLAT is enabled and which addresses and
prefix it is using. Add this information to the overview and to the
device/connection output. Example:

$ nmcli
  veth0: connected to clat
          "veth0"
          ethernet (veth), 4A:37:01:56:9D:AE, sw, mtu 1500
          ip4 default
          inet4 192.0.0.5/32
          route4 default metric 101
          inet6 2002:aaaa::64d4:2932:3585:7c89/64
          inet6 fe80::c060:8caf:f69b:e41a/64
          route6 fe80::/64 metric 1024
          route6 2002:aaaa::/64 metric 101
          route6 default via fe80::871:7ff:fe14:b7b9 metric 101
          clat inet4 192.0.0.5 inet6 2002:aaaa::2c0d:1e71:ef87:fac7 pref64 64:ff9b::/96

$ nmcli connection show clat
   ...
  IP4.ADDRESS[1]:                         192.0.0.5/32
  IP4.GATEWAY:                            0.0.0.0
  IP4.ROUTE[1]:                           dst = 0.0.0.0/0, nh = 0.0.0.0, mt = 101
  IP4.CLAT-ADDRESS:                       192.0.0.5
  IP6.ADDRESS[1]:                         2002:aaaa::64d4:2932:3585:7c89/64
  IP6.ADDRESS[2]:                         fe80::c060:8caf:f69b:e41a/64
  IP6.GATEWAY:                            fe80::871:7ff:fe14:b7b9
  IP6.ROUTE[1]:                           dst = fe80::/64, nh = ::, mt = 1024
  IP6.ROUTE[2]:                           dst = 2002:aaaa::/64, nh = ::, mt = 101
  IP6.ROUTE[3]:                           dst = ::/0, nh = fe80::871:7ff:fe14:b7b9, mt = 101
  IP6.CLAT-ADDRESS:                       2002:aaaa::2c0d:1e71:ef87:fac7
  IP6.CLAT-PREF64:                        64:ff9b::/96

Note how the IPv4 CLAT address is displayed both in IP4.ADDRESS and
IP4.CLAT-ADDRESS. That's because it is also configured in kernel. The
IPv6 CLAT address is not displayed in IP6.ADDRESS because it's not
configured in kernel.
2026-02-06 10:38:04 +01:00
Beniamino Galvani
d1598a10ec libnm: support the CLAT state
Make available the CLAT state in the NMIPConfig libnm objects.
2026-02-06 10:38:03 +01:00
Beniamino Galvani
f00030d79a core: export the CLAT state over D-Bus
Export over D-Bus the CLAT state: the IPv4 and IPv6 CLAT addresses and
the NAT64 prefix.
2026-02-06 10:38:02 +01:00
Beniamino Galvani
72cb5839fc core: l3cd: store the CLAT state
In the l3cd we already stored the CLAT administrative state (whether
we want to enable it or not) and the selected PREF64. Also store the
other current CLAT parameters, so that we can export them to clients
via D-Bus.
2026-02-06 10:38:00 +01:00
Beniamino Galvani
5c041cb891 l3cfg: send DAD solicitation for the IPv6 CLAT address
As per draft-ietf-v6ops-claton-14, hosts must perform duplicate
addresses detection (DAD) on the generated CLAT IPv6 address. This is
necessary not only to avoid address collisions but also because some
networks drop traffic from addresses that have not done DAD.

Since doing true DAD adds complexity, adopt the same approach as
Android: start DAD by sending a neighbor solicitation and don't wait
for any reply. This avoids the problem with dropped traffic; it
doesn't help with collisions, but collisions are anyway very unlikely
because the interface identifier is a random 64-bit value.

 5ae193ae36/clatd/main.c (363)
2026-02-06 10:37:59 +01:00
Beniamino Galvani
6d44237ed3 ndisc: track multiple PREF64 options
Previously the NMNDisc instance always used the last received NAT64
prefix. If a network advertises multiple NAT64 prefixes,
NetworkManager would constantly flip between them.  Change this and
keep a list of valid PREF64. Most importantly, stick with the same
PREF64 unless a new one appears from a router with higher priority, or
the current PREF64 expires.
2026-02-06 10:37:58 +01:00
Beniamino Galvani
fbfb5afec0 build: move the CLAT line in the meson summary
Move the CLAT line from the Miscellaneous section to the Features one.
2026-02-06 10:37:58 +01:00
Beniamino Galvani
aeeb52ab66 core: log message if CLAT is enabled but not supported
If CLAT is not supported (disabled at build time) and the
configuration enables it, log a message.
2026-02-06 10:37:57 +01:00
Beniamino Galvani
de42acd3fd core: print whether CLAT support is compiled in
At startup, print whether CLAT support is compiled in; it is useful
when debugging.
2026-02-06 10:37:57 +01:00
Beniamino Galvani
f2ced1e115 l3cfg: split updating CLAT config to a separate function
Split the CLAT code from _l3cfg_update_combined_config() so that the
function can be followed more easily.
2026-02-06 10:37:57 +01:00
Beniamino Galvani
cb09291635 nmcli: fix hiding default values
A property should be hidden when it has the default value and one of the
following conditions is met:

 - nmcli is called in "overview" mode (with flag -o)
 - the property has the HIDE flag

Previously, properties with the HIDE flag were always hidden. Fix
that.
2026-02-06 10:37:56 +01:00
Beniamino Galvani
0aab6ef1c0 merge: branch 'wifi-use-auth-retries'
wifi: respect connection.auth-retry for WPA connections

Closes #1316

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2308
2026-02-05 10:34:03 +00:00
Jan Fooken
a01000d811
NEWS: WPA connections now respect connection.auth-retry 2026-01-28 15:46:32 +01:00
Jan Fooken
b4fc8550f5
man: wifi: Document connection.auth-retry for WPA connections
Remove the mentioned limitation of limiting authentication retires to
802.1X connections and add information about the introduced secret
prompting behaviour.
2026-01-28 15:46:32 +01:00
Jan Fooken
746a5902ad
wifi: use authentication retry mechanism
While NetworkManager tries it's best to determine whether a new PSK is
needed, it can still run into edge cases.  One of these edge cases is that
a device can leave the range of an access point and therefore fail a 4-way
handshake.  Because these cases can't be confidently detected, a device
which was previously connected, should try to exhaust it's authentication
retries before requesting new secrets.  This leads to less user-facing
prompts while increasing the time from PSK change to prompt.
2026-01-28 15:46:26 +01:00
Jan Fooken
6dc51ddf01
device: add public method nm_device_auth_retries_has_next
Devices don't know whether they have authentication retries left,
so they can only make decisions ad-hoc after calling
nm_device_auth_retries_try_next.

Giving devices a way to determine whether the current attempt is their
last attempt, allows them to make decisions before failing a connection.
2026-01-28 15:42:20 +01:00
Jan Fooken
a3267aaf7b
device: add private getter for property auth-retries 2026-01-28 15:42:20 +01:00
Íñigo Huguet
871da67916 merge: branch 'ih/fix-api-version'
Bump version just after release, fix NM_API_VERSION on stable branches

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2337
2026-01-26 06:44:38 +00:00
Íñigo Huguet
f849163e82 nm-version: allow to define NM_VERSION_MAX_ALLOWED alone
Previously, if NM_VERSION_MIN_REQUIRED was not defined, it defaulted to
NM_VERSION. As a consequence, if NM_VERSION_MAX_ALLOWED was defined we
got a compilation error because MAX_ALLOWED < MIN_REQUIRED.

MAX_ALLOWED is used to get compilation warnings if you unintentionally
use a libnm's symbol introduced in a newer version. MIN_REQUIRED is used
to get rid of warnings about symbol deprecations.

Libnm users may want to use MAX_ALLOWED alone, because using a too new
symbol would fail to compile with older libnm. But they might want to
get deprecation warnings as soon as possible, so they want to leave
MIN_REQUIRED empty.
2026-01-26 06:44:00 +00:00
Íñigo Huguet
36275bc51c nm-version.h: use the right value of NM_API_VERSION
After the changes in release.sh in previous commits, during development
the value of NM_VERSION will always be the next version, not the latest
released one. As a consequence, we don't need to set MICRO+1 in
NM_API_VERSION, which was a temporary workaround.
2026-01-26 06:44:00 +00:00
Íñigo Huguet
c0fe80ff87 release: (manually) bump version to 1.57.2-dev
After the previous commits, release.sh bumps the version after tagging
the release, and not before. Therefore, it expects that the version is
already the next one when doing the release.

Manually bump the version this time so release.sh sees the right value
the next time it's executed after these changes.
2026-01-26 06:44:00 +00:00
Íñigo Huguet
9a3462af99 release.sh: fix a few small bugs and typos
Fix typo freedestkop -> freedesktop.

Removed unused argument of check_news (additionally, it was incorrectly
using @ instead of $).

Fixed incorrect use of `$? = 0` that was always successful.
2026-01-26 06:44:00 +00:00
Íñigo Huguet
5666407f15 release.sh: bump version after release
After tagging a release, create a commit bumping to the next version.
This effectively ends the change in the logic initiated in the previous
commit, from "bump version, then release" to "release, then bump
version".

The purpose of this is to have the right version set in nm_version.h and
nm_version_macros.h between two releases. Without this change, when we
introduced a new symbol, thus using the NM_AVAILABLE_IN_1_XX annotations,
we got compilation warnings until we did the next release (making the CI
to be red when configured the compilation to fail on warnings).
2026-01-26 06:44:00 +00:00
Íñigo Huguet
3a3a8ea59d release.sh: assume that the version is already the right one
Don't bump the version before tagging the release. Instead, assume that
it's already correctly set. This is in preparation for the next commit
where we will bump the version after the release, not before.

But don't assume that in the case of rc1 and major releases. For rc1 we
switch from devel releases to RC releases, and in major we switch from
RC releases to stable releases. For example, when we are going to
release 1.58-rc1, the current version will be 1.57.X-dev, so we need to
bump to 1.58-rc1. When we're going to release 1.58.0, the current
version will be 1.58-rcX, so we need to bump to 1.58.0.
2026-01-26 06:44:00 +00:00
Íñigo Huguet
d56cd26aea release.sh: add comments 2026-01-26 06:44:00 +00:00
Beniamino Galvani
e311df0c75 merge: branch 'feature/mstrodl/clat'
Add support for CLAT using a BPF program

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2107
2026-01-24 09:32:53 +00:00
Beniamino Galvani
c32f0fb71f l3cfg: fix the metric of the CLAT default route
Previously the metric of the CLAT default route was set to the IPv6
route metric plus 50. Instead:

 - If there is another non-CLAT default route on the device, use the
   same metric plus 1, so that native connectivity is always
   preferred.

 - Otherwise, use the metric from the "ipv4.route-metric" property of
   the connection profile.
2026-01-24 09:45:01 +01:00
Beniamino Galvani
2c896713b8 bpf: clat: add macros for header sizes
They make the code more compact and readable.
2026-01-24 09:44:59 +01:00
Beniamino Galvani
29eb48d7f9 bpf: clat: ensure data is pulled for direct packet access
There is no guarantee that the part of the packet we want to read or
write via direct packet access is linear. From the documentation of
bpf_skb_pull_data():

  For direct packet access, testing that offsets to access are within
  packet boundaries (test on skb->data_end) is susceptible to fail if
  offsets are invalid, or if the requested data is in non-linear parts
  of the skb. On failure the program can just bail out, or in the case
  of a non-linear buffer, use a helper to make the data available. The
  bpf_skb_load_bytes() helper is a first solution to access the
  data. Another one consists in using bpf_skb_pull_data to pull in
  once the non-linear parts, then retesting and eventually access the
  data.

See: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2107#note_3288979

Reported-by: DasSkelett <dasskelett@dasskelett.dev>
2026-01-24 09:44:57 +01:00
Beniamino Galvani
0731d8f3e0 bpf: clat: drop clat_handler()
Avoid the additional function call and perform the needed checks
directly in clat_handle_v4() and clat_handle_v6(). It will make easier
to check that the packet is linear is the next commit.
2026-01-24 09:44:55 +01:00
Beniamino Galvani
2d41711033 bpf: clat: support the IPv6 fragment header
Convert IPv6 fragments into IPv4.

The PLAT fragments IPv4 packets larger than the IPv6 MTU size into
smaller IPv6 packets. The safest IPv6 MTU value to configure on a PLAT
is the minimum IPv6 MTU, 1280. Therefore, we can expect IPv6 fragments
to be quite common.
2026-01-24 09:44:53 +01:00
Beniamino Galvani
616e18e61b l3cfg: fix CLAT MTU handling
The current code takes the IPv6 MTU value from the IPv6 default
route. However, that value is always zero because NM doesn't set it
usually. Instead, it should use the IPv6 MTU sysctl value. The problem
is that at this point NM hasn't written the sysctl yet, and we need
some logic to find the actual value.

Reported-by: DasSkelett <dasskelett@dasskelett.dev>
2026-01-24 09:44:50 +01:00
Beniamino Galvani
5cbd79a9ba core: introduce separate ipv6 mtu values in l3cd
The current "ip6_mtu" field of a l3cd is the IPv6 MTU received via
RA. Rename it accordingly and introduce another "ip6_mtu_static" field
that contains the value set in the ipv6.mtu connection property. It's
not used yet, but it will be in a following commit.
2026-01-24 09:44:48 +01:00
Beniamino Galvani
3699558106 bpf: clat: use IPv4 dummy address for ICMPv6 messages with native source
When running a traceroute for an IPv4 address, the nodes before the
NAT64 gateway return ICMPv6 Time Exceeded messages with a source IPv6
address not belonging to the NAT64 prefix. Such messages would be
normally dropped by the CLAT because the source address can't be
translated. This behavior complicates troubleshooting.

Follow the recommendation of
draft-ietf-v6ops-icmpext-xlat-v6only-source-01 and translate the
source address to the dummy IPv4 192.0.0.8.
2026-01-24 09:44:46 +01:00
Beniamino Galvani
2888d4c800 bpf: clat: fix redirect for outgoing packets
bpf_redirect_neigh() looks up the next hop in the routing table and
then redirects the packet to the given ifindex. The problem is that
the routing table might contain a default route with lower metric on a
different device; in that case the FIB lookup returns a next hop on
the other device, and the packet can't be delivered.

Use bpf_redirect() instead; the IPv4 already has the right L2
destination because the IPv4 default route points to the IPv6 gateway.

Reported-by: DasSkelett <dasskelett@dasskelett.dev>
2026-01-24 09:44:44 +01:00
Beniamino Galvani
6ac6d4f14e rpm: disable CLAT on i686
There is no bpftool compiled for i686.
2026-01-24 09:44:43 +01:00
Beniamino Galvani
6ec321d21b l3cfg: use the tcx attachment for the clat program
The TCX attachment type was added in kernel 6.6 (October 2023) and it
replaces the Traffic Control (TC) BPF attachment, providing better
usability. Convert the l3cfg code to use it.
2026-01-24 09:44:41 +01:00
Beniamino Galvani
bd67cefaaa ipv4: improve logging for ipv4.dhcp-ipv6-only-preferred 2026-01-24 09:44:40 +01:00
Beniamino Galvani
13cf12dd6e ipv4: enable by default ipv4.dhcp-ipv6-only-preferred when CLAT is on
When CLAT is enabled, we want to also enable and honor by default DHCP
option 108 (IPv6-only preferred), so that the host can avoid
requesting an IPv4 address and go IPv6-only.
2026-01-24 09:44:38 +01:00
Beniamino Galvani
193e37b410 bpf: clat: improve debug messages 2026-01-24 09:44:37 +01:00
Beniamino Galvani
c93ce65467 bpf: clat: translate inner headers of incoming ICMPv6 errors
ICMPv6 error messages contain a copy of the original packet that
caused the error. In a 464XLAT deployment, this inner packet is an
IPv6 packet (as translated by the PLAT), while the local host expects
to see the original IPv4 packet it generated.

Without translation, the local host can't match the error to an active
socket. This breaks functionality like Path MTU Discovery (PMTUD),
traceroute, and error reporting for connected UDP sockets.

This commit implements the translation of the inner headers from IPv6
to IPv4 for incoming ICMPv6 errors.

Some implementation notes:

 - this only handles incoming ICMPv6; outgoing ICMPv4 is not yet
   implemented, but it seems less important.

 - the program uses different functions for rewriting the outer and
   inner header. I tried using recursion but the verifier didn't seem
   to like it.

 - after rewriting the inner headers, the ICMP checksum is
   incrementally updated based on difference of all the individual
   modifications done to the inner headers. This has the advantage
   that all the operations are fixed-size. But probably it would be
   easier and faster to just calculate the checksum from scratch.
2026-01-24 09:44:36 +01:00
Beniamino Galvani
6f29305575 clat: support all pref64 lengths
Support all the prefix lengths defined in RFC 6052.
2026-01-24 09:42:36 +01:00
Beniamino Galvani
8414afd9ae clat: pass the configuration as a BPF global variable
The program only needs to know the local IPv4 address, the local IPv6
address and the PREF64. There is no need to create multiple maps for
that, just pass a global configuration struct containing those 3
fields.
2026-01-24 09:42:35 +01:00
Beniamino Galvani
8c83367a49 bpf: clat: improve the code style and consistency
Improve the code style and consistency of some functions:

- declare only one variable per line
- add "const" keyword to read-only function arguments
- remove unneeded function arguments
- rename variables holding headers on the stack with the "_buf"
  suffix
2026-01-24 09:42:34 +01:00
Beniamino Galvani
183d68dcbe bpf: clat: rework to avoid pointer arithmetic
Avoid using pointer arithmetic in the BPF program, so that it requires
only CAP_BPF and not CAP_PERFMON. In this context "pointer arithmetic"
means adding a variable value to a packet pointer. This means that the
program no longer tries to parse variable-size headers (IPv4 options,
IPv6 extension headers). Those were already not supported before. It
also doesn't parse VLAN tags, but there should be no need for that. If
we use fixed offset, we can avoid using the parsing helpers from
libxdp.
2026-01-24 09:42:33 +01:00
Beniamino Galvani
173dc154a0 bpf: clat: remove commented code
The rewrite of IPv6 header inside a ICMP error needs to be
implemented. Remove the unused comments for now.
2026-01-24 09:42:32 +01:00
Beniamino Galvani
e99a6452be bpf: clat: fix error handling for IPv6 packets
There are 3 possible results from clat_translate_v6():

 1. the packet didn't match the CLAT IPv6 address and must be
   accepted;

 2. the packet matches but it is invalid and so it must be dropped;

 3. the packet matches and it is valid; clat_handle_v6() should
    translate the packet to IPv4;

Before, the function returned TC_ACT_SHOT for both 2 and 3. Therefore,
clat_handle_v6() tried to rewrite also invalid packets.

Fix that by returning TC_ACT_UNSPEC for valid packets, meaning that
there isn't a final verdict yet.
2026-01-24 09:42:31 +01:00
Beniamino Galvani
232da41572 bpf: clat: don't explicitly inline functions
BPF handles function calls fine these days. Only leave the inline
qualifier on very small functions like csum_fold_helper().
2026-01-24 09:42:31 +01:00
Beniamino Galvani
213e9e33da bpf: clat: use the right endian-conversion function
bpf_ntohl() is more correct because the field is in network byte
order; but there is no actual change in behavior.
2026-01-24 09:42:30 +01:00
Beniamino Galvani
3af6761655 bpf: clat: fix translation of ICMPv6 Parameter Problem
According to RFC 6145 5.2, the pointer should be set for code 0, not
1.
2026-01-24 09:42:29 +01:00
Beniamino Galvani
6273f0afba bpf: clat: add missing "break" statements 2026-01-24 09:42:28 +01:00
Beniamino Galvani
d1351f1219 bpf: clat: remove unused includes 2026-01-24 09:42:27 +01:00
Beniamino Galvani
ade4de22f3 bpf: clat: remove unused variables 2026-01-24 09:42:27 +01:00
Beniamino Galvani
f9cd6e20a5 bpf: clat: fix other verifier errors
When copying the IPv6 addresses via a direct assignement, the compiler
generates 32-bit operations that the verifier doesn't like:

>   237: (61) r3 = *(u32 *)(r8 +76)       ; frame1: R3_w=pkt(r=0) R8=ctx()
>   ; .saddr = ip6h->saddr, @ clat.bpf.c:124
>   238: (63) *(u32 *)(r10 -64) = r3
>   invalid size of register spill

Use explicit memcpy() for those.

Also, check the packet length before accessing the ICMPv6 header.
2026-01-24 09:42:26 +01:00
Beniamino Galvani
815a795203 bpf: clat: avoid 32-bit register spills when access skb->data
The verifier reports this error when accessing skb->data:

  ; void *data     = (void *)(unsigned long long)skb->data; @ clat.bpf.c:625
  (61) r2 = *(u32 *)(r1 +76)       ; frame1: R1=ctx() R2_w=pkt(r=0)
  (63) *(u32 *)(r10 -120) = r2
  invalid size of register spill

Apparently it's trying to spill only 32 bits from the register to the
stack, which is invalid. A similar problem was reported here:
https://github.com/cilium/cilium/pull/25336

Add some macros using inline asm to fix the problem. With this change
now the compiler properly generates 64-bit spills.

 ; src/core/bpf/clat.bpf.c:625
-;     void *data     = (void *)(unsigned long long)skb->data;
+;     void *data     = SKB_DATA(skb);
      137:      61 12 4c 00 00 00 00 00 w2 = *(u32 *)(r1 + 0x4c)
-     138:      63 2a 88 ff 00 00 00 00 *(u32 *)(r10 - 0x78) = w2
+     138:      7b 2a 88 ff 00 00 00 00 *(u64 *)(r10 - 0x78) = r2
2026-01-24 09:42:25 +01:00
Mary Strodl
b5f534d31d NEWS: Note CLAT support 2026-01-24 09:42:22 +01:00
Beniamino Galvani
75c423f4c8 core: honor the ipv4.clat property 2026-01-24 09:42:01 +01:00
Beniamino Galvani
f11fb6dafc libnm,nmcli: add a new ipv4.clat property 2026-01-24 09:41:58 +01:00
Beniamino Galvani
ee1c91bbc8 ndisc: add support for PREF64 option (fixes) 2026-01-24 09:40:50 +01:00
Mary Strodl
4409c3d99a ndisc: add support for PREF64 option 2026-01-24 09:40:49 +01:00
Beniamino Galvani
76c18081d6 Add support for CLAT to l3cfg (fixes) 2026-01-24 09:40:49 +01:00
Mary Strodl
f0e77a4354 Add support for CLAT to l3cfg 2026-01-24 09:40:48 +01:00
Beniamino Galvani
ebb86ed2dd Add CLAT BPF program and build machinery (fixes) 2026-01-24 09:40:48 +01:00
Mary Strodl
fa9c00b595 Add CLAT BPF program and build machinery 2026-01-24 09:40:47 +01:00
Mary Strodl
dd3758dd80 contrib: Add libbpf and libxdp to dependencies
Required for CLAT support
2026-01-24 09:40:47 +01:00
Mary Strodl
83317fed4e l3-config-data: make get_direct_route_for_host public 2026-01-24 09:40:47 +01:00
Mary Strodl
afae4ddaf4 clat: propagate network_id down to l3cfg 2026-01-24 09:40:47 +01:00
Beniamino Galvani
5150a666cd netns: add a CLAT IP reservation type
This will be used to obtain an IPv4 address to be used for the CLAT
(464XLAT).

Based on a patch by Mary Strodl <ipadlover8322@gmail.com>.
2026-01-24 09:40:47 +01:00
Beniamino Galvani
c228427ae2 netns: allow defining a ip reservation that wraps around
The current implementation returns IP addresses obtained by adding a
counter to a base address. For CLAT we want to return all the 8
addresses in the 192.0.0.0/29 range, but not starting from 192.0.0.0
because that looks more like a network address. Slightly tweak the
algorithm so that addresses can wrap around.
2026-01-24 09:40:46 +01:00
Íñigo Huguet
87ee398db3 merge: branch 'update_ignored_phrases_and_words_in_product_names'
Update ignored phrases and words in product names

Closes #1863

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2346
2026-01-23 09:06:17 +00:00
Robert Schlabbach
7944f80f04 Update ignored phrases and words in product names
Update the lists of ignored phrases and words which are to be stripped
from product names, to remove product capabilities, the bus the product
attached on and similar nonsense.

Add tests for the product names which these additions are intended for.
2026-01-23 09:05:58 +00:00
Íñigo Huguet
5b6776089d merge: branch 'patch-3'
Update sl.po (Slovenian)

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2341
2026-01-23 08:59:29 +00:00
filmsi
4567c8e40f Replace sl.po (Slovenian) 2026-01-23 08:58:26 +00:00
Íñigo Huguet
85ba4d7c53 merge: branch 'main'
wwan: Ensure we get existing objects on reset

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1957
2026-01-23 08:55:10 +00:00
Cédric Bellegarde
8f9bc6af94 wwan: Remove GDBusObjectManagerClient workaround
Tested with no ModemManager in the bus.

NetworkManager is receiving object-added signal. So hack not needed
anymore.
2026-01-23 08:03:51 +00:00
Beniamino Galvani
63e9b804e9 merge: branch 'rr/select-device-entry'
nmtui: select device entry when adding/editing connection

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2345
2026-01-22 19:55:56 +00:00
Rahul Rajesh
e10fac49bb nmtui: use select button to select available devices
Since it is error prone to manually type in interface names to match existing
ones, we introduce a select button that allows a user to chose from a list of devices.

- Show "Select..." button for physical devices to choose from available
  devices in a popup dialog.
- devices are sorted in alphabetical order.
- Only for physical devices (ethernet, infiniband, wifi, etc)

Resolves: https://issues.redhat.com/browse/RHEL-129186
2026-01-22 09:50:21 -05:00
Jan Vaclav
574411b8a5 vpn: wait for device to become available before creating l3cd
In some situations, we will have a defined interface index, but
no device, because the idle source was not processed yet.

Reschedule _check_complete() in an idle source, so that it runs
after the device is processed.

Fixes: 306f9c490b ('vpn: Use nm_device_create_l3_config_data_from_connection if possible')
Resolves: https://issues.redhat.com/browse/RHEL-125796

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2347
2026-01-22 14:51:58 +01:00
Beniamino Galvani
e776f80197 merge: branch 'bg/safe-file-access-syms-main' 2026-01-20 15:17:21 +01:00
Beniamino Galvani
a550828f76 libnm: add safe file access backported symbols from 1.54.3
Add to main branch symbols for safe file access that were
backported to 1.54.3 to allow seamless upgrading from 1.54 to 1.58.
2026-01-20 15:07:11 +01:00
Beniamino Galvani
cd223e6696 libnm: add safe file access backported symbols from 1.52.2
Add to main branch symbols for safe file access that were
backported to 1.52.2 to allow seamless upgrading from 1.52 to 1.58
2026-01-20 15:06:42 +01:00
Beniamino Galvani
c254c4df21 nmtui: fix build error
Fix the following error:

  In function ‘_nm_auto_unref_ptrarray’,
      inlined from ‘nmt_connect_connection_list’ at ../src/nmtui/nmtui-connect.c:593:34,
      inlined from ‘nmtui_connect’ at ../src/nmtui/nmtui-connect.c:673:16:
  ../src/libnm-std-aux/nm-std-aux.h:1106:12: error: ‘all_active_wifi_devices’ may be used uninitialized [-Werror=maybe-uninitialized]
   1106 |         if (*v)                               \
        |            ^
  ../src/nmtui/nmtui-connect.c: In function ‘nmtui_connect’:
  ../src/nmtui/nmtui-connect.c:593:34: note: ‘all_active_wifi_devices’ was declared here
    593 |     gs_unref_ptrarray GPtrArray *all_active_wifi_devices;

Fixes: 221da3f8c0 ('nmtui: feature: wifi-rescan feature for the nmtui.')
2026-01-20 14:11:54 +01:00
Beniamino Galvani
748be9a3e7 cloud-setup: fix format string
On a i686 machine the build fails with:

../src/nm-cloud-setup/main.c: In function ‘_oci_new_vlan_dev’:
../src/nm-cloud-setup/main.c:800:47: error: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘gssize’ {aka ‘int’} [-Werror=format=]
  800 |     macvlan_name  = g_strdup_printf("macvlan%ld", config_data->iface_idx);
      |                                             ~~^   ~~~~~~~~~~~~~~~~~~~~~~
      |                                               |              |
      |                                               long int       gssize {aka int}
      |                                             %d
../src/nm-cloud-setup/main.c:801:42: error: format ‘%ld’ expects argument of type ‘long int’, but argument 3 has type ‘gssize’ {aka ‘int’} [-Werror=format=]
  801 |     connection_id = g_strdup_printf("%s%ld", connection_type, config_data->iface_idx);
      |                                        ~~^                    ~~~~~~~~~~~~~~~~~~~~~~
      |                                          |                               |
      |                                          long int                        gssize {aka int}
      |                                        %d

Fixes: 68d7e17737 ('Reapply "cloud-setup: create VLANs for multiple VNICs on OCI"')
2026-01-15 17:46:56 +01:00
Beniamino Galvani
9199c56f50 merge: branch 'bg/wifi-6ghz'
wifi: add support for new "6GHz" band

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2318
2026-01-15 16:44:06 +00:00
Beniamino Galvani
42e9cd1856 NEWS: update 2026-01-15 17:39:03 +01:00
Beniamino Galvani
499427a84e wifi: update the list of 5GHz channels
Update the list of Wi-Fi channels in the 5GHz band:

 - remove channels 7–16, which were part of 802.11j but were revoked
   in 2017;

 - remove the entries that are not valid as primary 20MHz channels but
   only as the center of bonded channels, e.g. 38, 42, etc.

 - add channel 144, introduced in the 802.11ac standard

Also restrict list of default channels for a 5GHz hotspot to those
that are available everywhere and without DFS.
2026-01-15 17:38:42 +01:00
Beniamino Galvani
c39b967a47 nmcli: print the band of wifi access points
It's a valuable information for users, especially because the channel
number can be ambiguous.

Before:
$ nmcli device wifi
IN-USE  BSSID              SSID   MODE   CHAN  RATE         SIGNAL  BARS  SECURITY
        42:00:00:AA:DD:CC  test   Infra  44    1170 Mbit/s  85      ▂▄▆█  WPA2
        92:00:00:AB:DD:CC  guest  Infra  44    1170 Mbit/s  85      ▂▄▆█  WPA2

After:
$ nmcli device wifi
IN-USE  BSSID              SSID   MODE   BAND     CHAN  RATE         SIGNAL  BARS  SECURITY
        42:00:00:AA:DD:CC  test   Infra  5 GHz    44    1170 Mbit/s  85      ▂▄▆█  WPA2
        42:00:00:AB:DD:CC  guest  Infra  5 GHz    44    1170 Mbit/s  85      ▂▄▆█  WPA2
2026-01-15 17:38:41 +01:00
Beniamino Galvani
7879acea8f libnm: export nm_utils_wifi_freq_to_band()
Clients typically want to show the band of an AP. The information is
already available because we export the frequency, but it is necessary
to implement some conversion logic.

Export libnm symbol nm_utils_wifi_freq_to_band() to do
that. Previously the function was used internally to generate the
value of the "band" string property from the frequency. For a public
function it is clearer if we return a enum value.
2026-01-15 17:38:41 +01:00
Thomas Makin
bb0a26e906 wifi: add support for new "6GHz" band
Until now the Wi-Fi bands were named after the first 802.11 standard
that introduced them: "a" for 5GHz introduced in 802.11a and "bg" for
2.4GHz introduced in 802.11b/g. With new bands added, this naming
scheme doesn't sound very intuitive to remember for users. Furthermore
we have now 6GHz that is introduced by 802.11ax (Wi-Fi 6), but the
compatible devices can use all three the bands (2.4, 5, 6 GHz).

For the 6 GHz band, simply name it "6GHz".

Co-authored-by: Beniamino Galvani <bgalvani@redhat.com>
2026-01-15 17:38:40 +01:00
Beniamino Galvani
5763b9b4de supplicant: fix center channel calculation
The formula is wrong for channels above 144 because the layout of the
80MHz channels is not regular. Use a lookup table.

Fixes: 7bb5961779 ('supplicant: honor the 'wifi.channel-width' property in AP mode')
2026-01-15 17:38:40 +01:00
Íñigo Huguet
ccc5f78fc4 merge: branch 'iwd-powersave'
device: Apply powersave configuration with iwd

Closes #1750

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2335
2026-01-15 08:55:43 +00:00
Vicki Pfau
5838c38b84 device: Apply powersave configuration with iwd
The powersave setting was apparently not touched at all in the iwd device,
so this adds the configuration, analogous to how the wifi device does.

Fixes #1750
2026-01-15 08:54:18 +00:00
Íñigo Huguet
57b6c1c5b7 merge: branch 'nmtui-wifi-rescan'
nmtui: feature: wifi-rescan feature for the nmtui.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2281
2026-01-15 07:55:48 +00:00
Rishabh Garg
221da3f8c0 nmtui: feature: wifi-rescan feature for the nmtui. 2026-01-15 07:55:48 +00:00
Íñigo Huguet
4cf6f0f9a8 merge: branch 'ih/spec_rm_snap'
spec: fix nmplugindir, remove snapshot and git_sha, move main info to the top

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2342
2026-01-13 07:14:16 +00:00
Íñigo Huguet
6d952902b9 spec: move the main info to the top
It's clearer this way, and it will allow to modify directly the
"Version:" and "Release:" fields to bump the version. It is more aligned
with the layout of other projects' spec files too.
2026-01-12 08:35:29 +01:00
Íñigo Huguet
5445ad2287 spec: remove snapshot and git_sha macros
Snapshot is only used from nm-copr-build.sh script, so not very useful.
Git_sha is used from build.sh. Other than that, downstream is always
nil.

Remove them and modify build.sh to use --define "dist xxx" instead of
them. This change is motivated by Packit not being able to modify the
release number if it has the %{snap} suffix.
2026-01-12 08:35:24 +01:00
Íñigo Huguet
9ebc8aa480 spec: fix nmplugindir
When dist_version is defined in meson, NM installs plugins to a
directory called `NetworkManager-${dist_version}`. If the dist version
contains a `~`, like `1.56~rc1`, defining nmplugindir with
`%{version_no_tilde}` makes it `NetworkManager-1.56-rc1`, causing
rpmbuild errors due to the mismatch.

Fix it by defining nmplugindir with `%{version}` instead.

Fixes: d975389bcd ('spec: use versioning scheme with ~dev and ~rc suffixes')
2026-01-09 08:44:37 +01:00
Beniamino Galvani
7cae64ac02 merge: branch 'bg/supplicant-blob-size'
supplicant: properly validate blobs

Closes #1850

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2334
2026-01-07 17:23:02 +00:00
Beniamino Galvani
eb784c3f27 supplicant: properly validate blobs
The purpose of the validation is to check that we pass to the
supplicant a configuration that it can understand. For certificates
and keys we enforce a maximum length of 64KiB; that means that the
value of the property we send (i.e. the file path or the blob id) can
be at most 64KiB. Instead we wrongly checked the size of the blob
data.

Fix the validation. Also, enforce a maximum blob size of 32MiB.

Fixes: e85cc46d0b ('core: pass certificates as blobs to supplicant for private connections')
2026-01-07 18:20:43 +01:00
Beniamino Galvani
c4b39914c4 core: limit the result from the helper to 32MiB 2026-01-07 18:20:43 +01:00
Íñigo Huguet
99514da050 merge: branch 'ih/versions-packit'
versioning: use everywhere the versioning scheme from the Git tags

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2332
2026-01-07 10:26:10 +00:00
Íñigo Huguet
4ce317a261 NEWS: update 2026-01-07 10:25:56 +00:00
Íñigo Huguet
9f4261168d release.sh: use versioning scheme with -dev and -rc suffixes
The previous commits has unified the versioning scheme to only use the
version names like 1.56-rc2, 1.56.2 and 1.57.1-dev, like the version
names that we use in the Git tags. The scheme with micro>=90 for RCs
will be used only internally, in the C headers. The tarballs will be
named with the new scheme.

Adapt the release.sh script to correctly understand this versioning
scheme and to create the tarballs with the right new name.

This will enable us to use Packit to automate rpm updates.
2026-01-07 10:25:56 +00:00
Íñigo Huguet
d975389bcd spec: use versioning scheme with ~dev and ~rc suffixes
In the previous commit meson.build was adapted to use versions with -dev
and -rc suffixes, as we create them in the Git tags, instead of versions
with micro>90 for RCs as we used to do. The tarball name will contain
the version with the new scheme, so adapt the spec file for it.

This will enable us to use Packit to do automatic updates.
2026-01-07 10:25:56 +00:00
Íñigo Huguet
e422b1c3d9 meson: specify project version with the -dev and -rc suffixes
This will create the tarball with names NetworkManager-1.56-rc2.tar.xz
or NetworkManager-1.57.1-dev.tar.xz. This way they will match with the
name of the Git tag, making easier for users, and specially for tools
like Packit, to understand the versioning scheme.

The goal is to make that there is only one public versioning scheme, the
one with -rc and -dev suffixes. Version numbers with micro>=90 for RC
releases is kept only as an internal thing for the C headers. Users of
the API can still use it.

Bump meson version to 0.56 to use str.substring().
2026-01-07 10:25:56 +00:00
Beniamino Galvani
19ac54fbd6 merge: branch 'bg/builtin-ping'
Use an internal "ping" implementation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2328
2026-01-07 09:16:19 +00:00
Beniamino Galvani
6b062dfeb5 rpm: drop weak dependency on iputils 2026-01-07 09:49:36 +01:00
Beniamino Galvani
de8d74aa08 device: use the internal ping implementation
Currently NetworkManager depends on the external ping binary to
perform the reachability check on IP addresses. This means that the NM
daemon package must depend on another package. On Fedora the iputils
package is 800KiB.

Implement the same functionality natively so that we can drop such
dependency.
2026-01-07 09:49:32 +01:00
Beniamino Galvani
90ea2ddb90 core: introduce nm_utils_ping_host()
Introduce a function that pings a given host. It opens a "ping socket"
(IPPROTO_ICMP), binds it to the given ifindex, connects it to the
remote address, and keep sending ICMP echo-request packets until it
receives a reply or the optional timeout is reached. By using this
kind of socket, the kernel automatically sets the ICMP ID on outgoing
packets and matches incoming packets by the same ID.
2026-01-07 09:48:00 +01:00
Íñigo Huguet
b416156b10 merge: branch 'bulgarian'
Update Bulgarian translation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2331
2025-12-24 15:10:40 +00:00
twlvnn
9febe04761 Update Bulgarian translation 2025-12-24 15:08:56 +00:00
Íñigo Huguet
71b9d83349 merge: branch 'main'
Update Turkish translation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2336
2025-12-24 15:08:10 +00:00
Muhammet Kara
664ba417c7 Update Turkish translation
Signed-off-by: Muhammet Kara <muhammetk@gmail.com>
2025-12-22 02:00:43 +03:00
Beniamino Galvani
a31a644f8b merge: branch 'bg/deprecate-wext'
build: deprecate Wireless Extensions

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2309
2025-12-18 15:14:45 +00:00
Beniamino Galvani
a45163b131 rpm: disable wireless extensions on Fedora
The Fedora kernel is built without WEXT since many years:

https://fedoraproject.org/wiki/Changes/RemoveWirelessExtensions
2025-12-18 16:06:44 +01:00
Beniamino Galvani
de1fcdcc72 build: deprecate Wireless Extensions
Wireless Extensions is the legacy, ioctl-based kernel interface used
to configure Wi-Fi cards. It has been deprecated and replaced by the
cfg80211/nl80211 API since 2007, as it doesn't support modern Wi-Fi
encryption and technologies. Mark it as deprecated, so that we can get
rid of some unmaintained and untested code in a future release.
2025-12-18 16:06:42 +01:00
Beniamino Galvani
a2d147366c Merge branch 'bg/issue1688'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2323
2025-12-17 11:59:20 +01:00
Beniamino Galvani
427a7cf257 nmcli: start the agent only after updating the connection
When connecting to a wifi network and providing the password on the
command line, nmcli first looks if there is a compatible connection to
reuse. If there is not, it creates and activates a new one via a
single call to AddAndActivate().

If there is a compatible connection, nmcli first calls Update() on it
to set the new password and then Activate() to bring it up. Before
that, it registers a secret agent that can prompt for a new password
in case of authentication failure.

However, as soon as nmcli registers a secret agent, NM tries to
activate again the connection if it was blocked due to a previous
authentication failure. This connection attempt is going to fail
because it still uses the old password, as new one hasn't been set via
Update().

Change the order of operations to register the agent after Update()
and before Activate().

Reproducer:

 nmcli device wifi connect SSID password BAD_PASSWORD
 nmcli device wifi connect SSID password GOOD_PASSWORD

Fixes: c8ff1b30fb ('nmcli/dev: use secret agent for nmcli d [wifi] connect')
2025-12-17 10:55:51 +01:00
Beniamino Galvani
3a4e18e302 nmcli: fix "device wifi connect" command with existing connection
Executing this command twice, or when a connection profile already
exists for the SSID:

  nmcli device wifi connect $SSID password $PASSWORD

returns error:

  Error: 802-11-wireless-security.key-mgmt: property is missing.

When setting the password nmcli was wiping the existing wireless
security setting.

Fixes: c8ff1b30fb ('nmcli/dev: use secret agent for nmcli d [wifi] connect')

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1688
2025-12-17 10:55:50 +01:00
Íñigo Huguet
8e0825f9cd release: bump version to 1.57.1 (development) 2025-12-12 16:01:44 +01:00
Íñigo Huguet
df8288de7f merge: branch 'ih/strerror_r'
Fix two compilation issues

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2330
2025-12-12 14:36:58 +00:00
Íñigo Huguet
118475d571 ci: build with -D systemdsystemgeneratordir=no
Avoid build failures in some distros.
  ERROR: Assert failed: systemd required but not found, please provide a valid systemd user generator dir or disable it

Fixes: 636fb5ef24 ('systemd: install initrd services using a generator')
2025-12-12 15:24:09 +01:00
Íñigo Huguet
599cc1ed1d 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')
2025-12-12 15:07:53 +01:00
Íñigo Huguet
1756ec54e3 merge: branch 'issue1809'
CVE-2025-9615: avoid that non-admin user using other users' certificates.

Closes #1809

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2324
2025-12-12 12:29:41 +00:00
Beniamino Galvani
1a52bbe7c9 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.
2025-12-12 12:43:15 +01:00
Beniamino Galvani
57eb4a5bc6 vpn: check that plugin supports private connections
Only allow private VPN connections if the VPN plugin declares the
supports-safe-private-file-access capability. Also check that the
private connection doesn't have more than one owner.
2025-12-12 12:42:01 +01:00
Beniamino Galvani
10db4baeb6 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.
2025-12-12 12:41:28 +01:00
Beniamino Galvani
8d8edda3f4 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.
2025-12-12 12:38:50 +01:00
Beniamino Galvani
e85cc46d0b 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.
2025-12-12 12:38:50 +01:00
Beniamino Galvani
a1928b4459 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.
2025-12-12 12:38:49 +01:00
Beniamino Galvani
9703305122 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.
2025-12-12 12:38:49 +01:00
Beniamino Galvani
932b85f7e7 supplicant: rename variables
Rename uid to to blob_id, and con_uid to con_uuid.
2025-12-12 12:38:49 +01:00
Beniamino Galvani
4e26403c4a 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.
2025-12-12 12:38:49 +01:00
Beniamino Galvani
bd2484d1a9 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.
2025-12-12 12:38:48 +01:00
Beniamino Galvani
41e28b900f 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.
2025-12-12 12:38:48 +01:00
Beniamino Galvani
6c1e04fc61 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.
2025-12-12 12:38:48 +01:00
Beniamino Galvani
2739850b78 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.
2025-12-12 12:38:48 +01:00
Íñigo Huguet
d8f143f601 spec: enable polkit_noauth_group for Fedora <= 43 and RHEL <= 10
In Fedora 44 and RHEL 11, admin users will need to type their password
even on local consoles.
2025-12-12 12:38:48 +01:00
Íñigo Huguet
39143f8bdd polkit: add build option to allow admin users not to type their password
Add a build option to allow installing a Polkit rule that will grant
permissions for admin users without asking for their password if they're
in a local console.

This shouldn't be encouraged, though. It's common practice that admin
users has to introduce their password to make system-wide changes. The
standard polkit policy, without this rule, is auth_admin_keep. This
policy will ask for the password once and won't ask for it again for
~5 minutes, so it is not too unconvenient.

Different distros use different group names for users with admin rights,
typically 'sudo' or 'wheel'. The build option allows to define the
desired group, or to leave it empty to not install the rule.

However, until the previous commit it was allowed that local users (even
non-admin) could do system-wide changes without introducing a password.
This option allows to maintain the same behavior for admin users,
keeping backwards compatibility so we avoid breaking existing scripts,
for example. We cannot achieve the same for non-admin users because
allowing them to create system-wide connection causes security
vulnerabilities that cannot be fixed in any other way.
2025-12-12 12:38:48 +01:00
Íñigo Huguet
0b75d905e5 polkit: remove the modify_system build option
This build option allowed non-admin users to create system-wide
connections. Generally, this is not a good idea as system-wide changes
should be done by administrators.

However, the main reason for the change is that this can be used to
bypass filesystem permissions, among possibly other attacks. As the
daemon runs as root, a user can create a system-wide connection that
uses a certificate from a different user to authenticate in a WiFi
network protected with 802.1X or a VPN, because as root user the daemon
can access to the file.

This patch does not completely fix the issue, as users can still create
private connections specifying a path to another user's connection. This
will be addressed in other patch. However, this patch is needed too,
because in system-wide connections we don't store which user created the
connection, so there woudn't be any way to check his/her permissions.

This is part of the fix for CVE-2025-9615

See: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1809
2025-12-12 12:38:48 +01:00
Íñigo Huguet
13bfa44ceb 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.
2025-12-12 12:17:58 +01:00
Beniamino Galvani
dad4da06b1 libnm-core: fix the documentation of the gateway IP property
The D-Bus API documentation of the IPv4 and IPv6 settings say:

* addresses
 	Deprecated in favor of the 'address-data' and 'gateway'
 	properties, but this can be used for backward-compatibility
 	with older daemons. Note that if you send this property the
 	daemon will ignore 'address-data' and 'gateway'.

* gateway
       The gateway associated with this configuration. This is only
       meaningful if "addresses" is also set.

This documentation wrongly suggests that at D-Bus level "gateway"
requires "addresses", while it actually requires "address-data". The
reason for the inconsistency is that the gateway documentation is
common between nmcli and D-Bus and it refers to the "address" GObject
property, not to the D-Bus property.

Fix this inconsistency by not explicitly mentioning the property name.

Fixes: 36156b70dc ('libnm: Override parts of nm-setting-docs.xml')

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2319
2025-12-04 16:40:07 +01:00
Jan Vaclav
0b61924048 merge: branch 'jv/fix-rawhide-build'
all: fix NM compilation on rawhide

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2321
2025-12-04 12:01:18 +00:00
Jan Vaclav
d40e88fd02 test-link: test bond with use_carrier=1
`use_carrier` is removed from kernel since 6.18 [1], and returns
the following error if set to 0:
> option obsolete, use_carrier cannot be disabled

This causes a failure of test-link-linux, so let's set it to 1.

[1] https://lore.kernel.org/all/2029487.1756512517@famine/
2025-12-04 11:51:28 +00:00
Jan Vaclav
8e72e6b4fb aux-intern: add explicit cast for strchr()
`addr` is always reallocated in this branch, so it
is safe to cast the result of strchr to char* here
to silence the const-qualifier warning.
2025-12-04 11:51:28 +00:00
Jan Vaclav
9e70f31c8c initrd: remove const qualifier from temporary variable
`argument` is not const, but `tmp` is. We use `tmp`
for reading arguments one by one, but we cannot add
a null byte to separate the key and value if it is const.

Make it non-const, so that `val[0] = '\0';` does not fail.
2025-12-04 11:51:28 +00:00
Jan Vaclav
ac427b25fb core, impl: drop const qualifier from split outputs
We write into the buffer returned by nm_strsplit_set_full(), even
though it is returned as `const char**`. The function description
claims this is fine:

> *   It is however safe and allowed to modify the individual strings in-place,
> *   like "g_strstrip((char *) iter[0])".

Remove the const qualifier via cast so that it does not raise errors.
2025-12-04 11:51:28 +00:00
Jan Vaclav
754b87e1c4 supplicant: separate input and local value
We reallocate this value in the function, which is necessary
because we write into it, and the input is const.

Move the allocation into a local variable instead of overwriting
the input pointer, because we are also pointing to it via
`char* s`, which is not const.
2025-12-04 11:51:28 +00:00
Jan Vaclav
5f6beb0e57 nm-udev-utils: constify strstr-output variable
`subsystem_full` is const, so `s` needs to be const too.
Reorder the NULL-byte write so that we are not writing
into a const char* (the underlying memory is the same).
2025-12-04 11:51:28 +00:00
Jan Vaclav
487ca30256 all: const-ify str(r)chr output variables where possible 2025-12-04 11:51:28 +00:00
Jan Vaclav
a07961cfbe systemd: selectively backport "Fix constness issues with newer glibc"
NetworkManager is failing to build on Rawhide with the following errors:
../src/libnm-systemd-shared/src/basic/string-util.h:33:16: error: return discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
   33 |         return strstr(haystack, needle);
      |                ^~~~~~
In file included from ../src/libnm-systemd-shared/src/basic/fd-util.c:30:
../src/libnm-systemd-shared/src/basic/sort-util.h: In function ‘bsearch_safe’:
../src/libnm-systemd-shared/src/basic/sort-util.h:34:16: error: return discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
   34 |         return bsearch(key, base, nmemb, size, compar);
      |                ^~~~~~~

This is fixed in systemd by commit 0bac1ed2422f15308414dd1e9d09812a966b0348:
> Latest glibc uses _Generic to have strstr() and other functions return
> const char* or char* based on whether the input is a const char* or a
> char*. This causes build failures as we previously always expected a char*.
>
> Let's fix the compilation failures and add our own macros similar to glibc's
> to have string functions that return a mutable or const pointer depending on
> the input.

Selectively backport the changes we need to fix building.
2025-12-04 11:51:28 +00:00
Jan Vaclav
4e7e159224 merge: branch 'jv/unifcfg-scripts'
build/configure_for_system: stop building with ifcfg_rh support

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2320
2025-12-04 11:50:02 +00:00
Jan Vaclav
ae134ca9f4 build/configure_for_system: stop building with ifcfg_rh support
It has been deprecated for a long time, so it would be probably
fine to stop building with it enabled. RHEL 9 is excluded since
it still supports the ifcfg-rh format.
2025-12-04 11:33:13 +00:00
Jan Vaclav
55f96057c6 build/configure_for_system: disable building with team on rhel10+ 2025-12-04 11:33:13 +00:00
Jan Vaclav
de0a37b248 build: fix rhel version detection in configure_for_system 2025-12-04 11:33:13 +00:00
Beniamino Galvani
7315e7e0ee merge: branch 'saemismatch-signal-handlers'
core: add handlers for SaePasswordMismatch signal

Closes #904

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2293
2025-12-03 16:08:41 +00:00
Mitchell Augustin
bcb96a1b19 core: add handlers for SaePasswordMismatch signal
Trigger a new auth request to the user when the SaePasswordMismatch
signal is received from wpa_supplicant.

Closes #904
2025-12-02 14:25:12 +00:00
Beniamino Galvani
561fff3c8d merge: branch 'main'
connectivity: add per-device check-connectivity setting

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2316
2025-12-02 09:55:34 +00:00
Popax21
a9f2c15663 connectivity: add per-device check-connectivity setting
Adds a new `check-connectivity` setting to the `device` section which can be
used to disable the connectivity check for a particular device.
2025-12-02 10:04:20 +01:00
Beniamino Galvani
8a9b17071b version: add 1.58 macros 2025-11-28 19:05:59 +01:00
Till Maas
c1519bd514 merge: branch 'update_pt_BR'
Update Brazilian translation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2307
2025-11-26 22:16:53 +01:00
Rafael Fontenelle
8b5a61458b Update Brazilian Portuguese translation
Co-authored-by: Matheus Barbosa <mdpb.matheus@gmail.com>
Co-authored-by: Samuel Schlemper <samuelschlemper2006@gmail.com>
Co-authored-by: Juliano de Souza Camargo <julianosc@pm.me>
2025-11-26 22:16:25 +01:00
Beniamino Galvani
9e01443b14 man: describe the allowed boolean values in NetworkManager.conf
The NetworkManager.conf man page is describing some options as
"whether X is enabled", without explicitly saying that it's a
boolean. Also, the allowed values are not mentioned. Clarify those
aspects.
2025-11-26 09:03:11 +01:00
Beniamino Galvani
78519589b9 NEWS: update 2025-11-26 09:03:08 +01:00
Till Maas
771f86105e merge: branch 'patch-2'
Updated Slovenian translation

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2317
2025-11-24 21:31:56 +01:00
filmsi
b3f9f52505 Updated Slovenian translation 2025-11-24 21:31:55 +01:00
Beniamino Galvani
b41a5ec2d4 merge: branch 'initrd-services-dbus-fix'
systemd: install initrd services using a generator

Closes #1814

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2312
2025-11-20 17:41:45 +00:00
Antonio Alvarez Feijoo
636fb5ef24 systemd: install initrd services using a generator
Since both `NetworkManager.service` and `NetworkManager-initrd.service` are
allocated for the same bus name (`org.freedesktop.NetworkManager`) and this is
not allowed, the best option is to use a systemd generator to install them only
in the initrd, instead of setting fixed Install sections.

Fixes #1814
2025-11-20 13:26:15 +00:00
Beniamino Galvani
d006d61aa1 merge: branch 'main'
core: restrict connectivity check lookups to per-link DNS if available

Closes #1836

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2313
2025-11-20 08:24:08 +00:00
Popax21
6e2de1d2b3 core: restrict connectivity check lookups to per-link DNS if available
Restrict connectivity check DNS lookups to just the relevant link if the link
has a per-link DNS resolver configured. This change was previously discussed as
part of issue
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1836, and
brings NM's behavior back in line with the behavior documented in the man page.

The connectivity check checks for a per-link DNS resolver by querying
systemd-resolved's `ScopeMask` for the link; this involves a small D-Bus
roundtrip, but is ultimately the more flexible solution since it is also capable
of dealing with per-link DNS configuration stemming from other sources.

Fixes: e6dac4f0b6 ('core: don't restrict DNS interface when performing connectivity check')
2025-11-19 14:54:40 +00:00
Beniamino Galvani
4afa00874f merge: branch 'mptcp-laminar'
mptcp: add `laminar` endpoint support and set it by default

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2314
2025-11-19 14:01:05 +00:00
Matthieu Baerts (NGI0)
3ce1da1fd2 NEWS: new MPTCP 'laminar' endpoint & default
A summary linked to the last two commits.

Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
2025-11-19 12:54:09 +00:00
Matthieu Baerts (NGI0)
8caa781270 mptcp: set the laminar flag by default
By default, the MPTCP limits have 'add_addr_accepted' set to 0. It means
that when the other peer announces an additional address it can be
reached from, the receiver will not try to establish any new subflows to
this address. If this limit is increased, and without the new 'laminar'
flag, the MPTCP in-kernel path-manager will select the source address by
looking at the routing tables to establish this new subflow.

This is not ideal: very likely, the source address will be the one
linked to the default route and a new subflow from the same interface as
the initial one will be created instead of using another path.

This is especially problematic when the other peer has set the 'C-flag'
in the MPTCP connection request (MP_CAPABLE). This flag can be set to
tell the other side that the peer will not accept extra subflows
requests sent to its initial IP address and port: typically set by a
server using an anycast address, behind a legacy Layer 4 load balancer.

It sounds better to add the 'laminar' flag by default to pick the source
address from well-defined MPTCP endpoints, rather than relying on
routing rules which will likely not pick the most interesting solution.

Note that older kernels will accept unsupported flags, and ignore them.
So it is fine to have the new flag added by default even if it is not
supported.

Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
2025-11-19 12:54:09 +00:00
Matthieu Baerts (NGI0)
2b03057de0 mptcp: add 'laminar' endpoint support
This new endpoint type has been recently added to the kernel in v6.18
[1]. It will be used to create new subflows from the associated address
to additional addresses announced by the other peer. This will be done
if allowed by the MPTCP limits, and if the associated address is not
already being used by another subflow from the same MPTCP connection.

Note that the fullmesh flag takes precedence over the laminar one.
Without any of these two flags, the path-manager will create new
subflows to additional addresses announced by the other peer by
selecting the source address from the routing tables, which is harder to
configure if the announced address is not known in advance.

The support of the new flag is easy: simply by declaring a new flag for
NM, and adding it in the related helpers and existing checks looking at
the different MPTCP endpoint. The documentation now references the new
endpoint type.

Note that only the new 'define' has been added in the Linux header file:
this file has changed a bit since the last sync, now split in two files.
Only this new line is needed, so the minimum has been modified here.

Link: https://git.kernel.org/torvalds/c/539f6b9de39e [1]
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
2025-11-19 12:54:09 +00:00
Beniamino Galvani
3b10b88290 core: fix rate-limit test failures
It's possible that the first timeout gets delayed; therefore the
interval between the first and the second callback can be less than
one second, and the budget doesn't refill completely.

Schedule the second timeout from the first callback to guarantee that
at least one second passes between the callbacks.

Fixes: ff0c4346fc ('core: add rate-limiting helper')
2025-11-18 15:02:05 +01:00
Íñigo Huguet
d687768c61 libnm: move hsr symbols to the right version
These symbols has been added to the 1.54.2 stable branch, so they are
actually available since then.
2025-11-18 10:39:49 +01:00
Íñigo Huguet
239b0fbbc9 release: bump version to 1.57.0 (development) 2025-11-17 16:42:05 +01:00
192 changed files with 46869 additions and 18420 deletions

1
.gitignore vendored
View file

@ -81,7 +81,6 @@ test-*.trs
/data/org.freedesktop.NetworkManager.service /data/org.freedesktop.NetworkManager.service
/data/server.conf /data/server.conf
/data/org.freedesktop.NetworkManager.policy /data/org.freedesktop.NetworkManager.policy
/data/org.freedesktop.NetworkManager.policy.in
/data/nm-sudo.service /data/nm-sudo.service
/data/nm-priv-helper.service /data/nm-priv-helper.service
/data/NetworkManager-config-initrd.service /data/NetworkManager-config-initrd.service

View file

@ -60,11 +60,11 @@ variables:
# #
# This is done by running `ci-fairy generate-template` and possibly bumping # This is done by running `ci-fairy generate-template` and possibly bumping
# ".default_tag". # ".default_tag".
ALPINE_TAG: 'tag-0c3a6f855fb8' ALPINE_TAG: 'tag-8e4bbc59695b'
CENTOS_TAG: 'tag-c1c23df75dda' CENTOS_TAG: 'tag-caf6673db1a7'
DEBIAN_TAG: 'tag-d4bf5db9e214' DEBIAN_TAG: 'tag-e394e8e726e1'
FEDORA_TAG: 'tag-c1c23df75dda' FEDORA_TAG: 'tag-caf6673db1a7'
UBUNTU_TAG: 'tag-d4bf5db9e214' UBUNTU_TAG: 'tag-e394e8e726e1'
ALPINE_EXEC: 'bash .gitlab-ci/alpine-install.sh' ALPINE_EXEC: 'bash .gitlab-ci/alpine-install.sh'
CENTOS_EXEC: 'bash .gitlab-ci/fedora-install.sh' CENTOS_EXEC: 'bash .gitlab-ci/fedora-install.sh'

View file

@ -12,9 +12,9 @@ Please read
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/CONTRIBUTING.md https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/CONTRIBUTING.md
before opening the merge request. In particular, check that: before opening the merge request. In particular, check that:
- [ ] the subject for all commits is concise and explicative - [ ] The subject for all commits is concise, explanatory, and includes a prefix indicating the area of code changed (e.g., "nmcli: ", "core: ")
- [ ] the message for all commits explains the reason for the change - [ ] The message for all commits explains the reason for the change
- [ ] the source is properly formatted - [ ] The source is properly formatted
- [ ] any relevant documentation is up to date - [ ] Any relevant documentation is up to date
- [ ] you have added unit tests if applicable - [ ] You have added unit tests if applicable
- [ ] the NEWS file is updated when the change deserves to be mentioned, for example for new features, behavior changes, API deprecations, etc. - [ ] The NEWS file is updated when the change deserves to be mentioned, for example for new features, behavior changes, API deprecations, etc.

61
NEWS
View file

@ -1,12 +1,64 @@
============================================= =============================================
NetworkManager-1.56 NetworkManager-1.58
Overview of changes since NetworkManager-1.54 Overview of changes since NetworkManager-1.56
============================================= =============================================
This is a snapshot of NetworkManager development. The API is
subject to change and not guaranteed to be compatible with
the later release.
USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* Unify the versioning to use everywhere the scheme with the -rcX or -dev * Unify the versioning to use everywhere the scheme with the -rcX or -dev
suffixes when appropriate. This affects, for example, the URL and filename suffixes when appropriate. This affects, for example, the URL and filename
of the release tarball and the version reported by nmcli and the daemon. of the release tarball and the version reported by nmcli and the daemon.
As an exception, the C API will continue to use the 90+ scheme for RC versions. As an exception, the C API will continue to use the 90+ scheme for RC versions.
* Restrict the connectivity check to use the DNS servers defined on the
same link. If the link has no DNS servers, the connectivity check will
use any servers available in the system.
* Install the systemd units in the initramfs using a systemd generator.
* A new "check-connectivity" configuration option is available to disable the
connectivity check for selected interfaces.
* Remove the modify_system build option that allowed setting up the
polkit permissions to allow non-admin users to create system-wide
connection. That configuration is discouraged because it can be used
to bypass filesystem permissions.
* 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.
* The support for Wireless Extensions is deprecated and will be
removed in a future release. Wireless Extensions are now disabled by
default.
* Use an internal implementation of the ping functionality when the
"connection.gateway-ping-timeout" or "connection.ip-ping-addresses"
properties are set, instead of relying on the "ping" tool.
* The powersave property now functions with the iwd backend.
* The "band" property of Wi-fi connections now accepts the "6GHz"
value.
* Show the Wi-Fi band of APs in the scan results from nmcli.
* New <Select...> button in nmtui that allows users to chose from list of
available devices when creating connection profiles for physical interfaces
(Ethernet, Wi-Fi, etc.).
* Add support for CLAT (464XLAT) using a BPF program.
* Change the default value of the ipv4.dhcp-ipv6-only-preferred property
to a new value "auto" which automatically enables the option when CLAT
is enabled ("yes" or "auto") in the connection profile.
* WIFI connections using wpa-psk respect the setting connection.auth-retry
and only prompt for new secrets during the last authentication attempt before
failing.
* Add support for GENEVE interface.
* The DHCPv4 internal client now ignores option 3 (Router) if the lease
contains option 121 (Classless Static Route), as recommended by RFC 3442.
* Allow persisting the managed state across reboots from nmcli and the D-Bus API.
* Allow changing the device's administrative state in the kernel at the same
time as a change to the managed state from nmcli and the D-Bus API.
=============================================
NetworkManager-1.56
Overview of changes since NetworkManager-1.54
=============================================
* nmcli now supports viewing and managing WireGuard peers. * nmcli now supports viewing and managing WireGuard peers.
* Support reapplying the "sriov.vfs" property as long as * Support reapplying the "sriov.vfs" property as long as
"sriov.total-vfs" is not changed. "sriov.total-vfs" is not changed.
@ -35,11 +87,6 @@ Overview of changes since NetworkManager-1.54
for eBPF is now detected at run time. for eBPF is now detected at run time.
* Add new MPTCP 'laminar' endpoint type, and set it by default alongside * Add new MPTCP 'laminar' endpoint type, and set it by default alongside
the 'subflow' one. the 'subflow' one.
* 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 NetworkManager-1.54

View file

@ -294,3 +294,6 @@
/* Define to 1 if dlvsym() is available */ /* Define to 1 if dlvsym() is available */
#mesondefine HAVE_DLVSYM #mesondefine HAVE_DLVSYM
/* Define to 1 if you want CLAT support. */
#mesondefine HAVE_CLAT

View file

@ -8,6 +8,7 @@ apk add \
'alpine-sdk' \ 'alpine-sdk' \
'autoconf' \ 'autoconf' \
'bash' \ 'bash' \
'bpftool' \
'clang' \ 'clang' \
'curl-dev' \ 'curl-dev' \
'dbus' \ 'dbus' \
@ -23,6 +24,7 @@ apk add \
'iproute2' \ 'iproute2' \
'iptables' \ 'iptables' \
'jansson-dev' \ 'jansson-dev' \
'libbpf-dev' \
'libgudev-dev' \ 'libgudev-dev' \
'libndp-dev' \ 'libndp-dev' \
'libnvme-dev' \ 'libnvme-dev' \

View file

@ -32,6 +32,7 @@ install_ignore_missing() {
install \ install \
\ \
bpftool \
clang \ clang \
dbus \ dbus \
dbus-x11 \ dbus-x11 \
@ -43,6 +44,7 @@ install \
iproute2 \ iproute2 \
iptables \ iptables \
libaudit-dev \ libaudit-dev \
libbpf-dev \
libcurl4-gnutls-dev \ libcurl4-gnutls-dev \
libdbus-1-dev \ libdbus-1-dev \
libgirepository1.0-dev \ libgirepository1.0-dev \

View file

@ -49,6 +49,7 @@ install \
ModemManager-glib-devel \ ModemManager-glib-devel \
audit-libs-devel \ audit-libs-devel \
bluez-libs-devel \ bluez-libs-devel \
bpftool \
clang \ clang \
dbus-devel \ dbus-devel \
dbus-x11 \ dbus-x11 \
@ -64,6 +65,7 @@ install \
iptables \ iptables \
jansson-devel \ jansson-devel \
jq \ jq \
libbpf-devel \
libcurl-devel \ libcurl-devel \
libndp-devel \ libndp-devel \
libnvme-devel \ libnvme-devel \

View file

@ -100,7 +100,17 @@ Release: __RELEASE_VERSION__%{?dist}
%else %else
%bcond_without iwd %bcond_without iwd
%endif %endif
%if 0%{?fedora} <= 43 || 0%{?rhel} <= 10
%bcond_without polkit_noauth_group
%else
%bcond_with polkit_noauth_group
%endif
%ifarch %{ix86}
# there is no bpftool in i686
%bcond_with clat
%else
%bcond_without clat
%endif
############################################################################### ###############################################################################
%global dbus_version 1.9.18 %global dbus_version 1.9.18
@ -178,7 +188,9 @@ Requires: dbus >= %{dbus_version}
Requires: glib2 >= %{glib2_version} Requires: glib2 >= %{glib2_version}
Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release} Requires: %{name}-libnm%{?_isa} = %{epoch}:%{version}-%{release}
Recommends: iputils %if %{with clat}
Requires: libbpf
%endif
%if 0%{?rhel} == 8 %if 0%{?rhel} == 8
# Older libndp versions use select() (rh#1933041). On well known distros, # Older libndp versions use select() (rh#1933041). On well known distros,
@ -227,6 +239,7 @@ Conflicts: NetworkManager-dispatcher-routing-rules <= 1:1.47.5-3
%endif %endif
BuildRequires: gcc BuildRequires: gcc
BuildRequires: clang
BuildRequires: pkgconfig BuildRequires: pkgconfig
BuildRequires: meson BuildRequires: meson
BuildRequires: gettext-devel >= 0.19.8 BuildRequires: gettext-devel >= 0.19.8
@ -281,6 +294,10 @@ BuildRequires: firewalld-filesystem
BuildRequires: iproute BuildRequires: iproute
BuildRequires: iproute-tc BuildRequires: iproute-tc
BuildRequires: libnvme-devel >= 1.5 BuildRequires: libnvme-devel >= 1.5
%if %{with clat}
BuildRequires: libbpf-devel
BuildRequires: bpftool
%endif
Provides: %{name}-dispatcher%{?_isa} = %{epoch}:%{version}-%{release} Provides: %{name}-dispatcher%{?_isa} = %{epoch}:%{version}-%{release}
@ -600,19 +617,20 @@ Preferably use nmcli instead.
%endif %endif
%if %{with wifi} %if %{with wifi}
-Dwifi=true \ -Dwifi=true \
%if 0%{?fedora}
-Dwext=true \
%else
-Dwext=false \
%endif
%else %else
-Dwifi=false \ -Dwifi=false \
%endif %endif
-Dwext=false \
%if %{with iwd} %if %{with iwd}
-Diwd=true \ -Diwd=true \
%else %else
-Diwd=false \ -Diwd=false \
%endif %endif
%if %{with clat}
-Dclat=true \
%else
-Dclat=false \
%endif
%if %{with bluetooth} %if %{with bluetooth}
-Dbluez5_dun=true \ -Dbluez5_dun=true \
%else %else
@ -649,7 +667,9 @@ Preferably use nmcli instead.
-Dselinux=true \ -Dselinux=true \
-Dpolkit=true \ -Dpolkit=true \
-Dconfig_auth_polkit_default=true \ -Dconfig_auth_polkit_default=true \
-Dmodify_system=true \ %if %{with polkit_noauth_group}
-Dpolkit_noauth_group=wheel \
%endif
-Dconcheck=true \ -Dconcheck=true \
%if 0%{?fedora} %if 0%{?fedora}
-Dlibpsl=true \ -Dlibpsl=true \
@ -659,6 +679,7 @@ Preferably use nmcli instead.
-Dsession_tracking=systemd \ -Dsession_tracking=systemd \
-Dsuspend_resume=systemd \ -Dsuspend_resume=systemd \
-Dsystemdsystemunitdir=%{_unitdir} \ -Dsystemdsystemunitdir=%{_unitdir} \
-Dsystemdsystemgeneratordir=%{_systemdgeneratordir} \
-Dsystem_ca_path=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \ -Dsystem_ca_path=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \
-Ddbus_conf_dir=%{dbus_sys_dir} \ -Ddbus_conf_dir=%{dbus_sys_dir} \
-Dtests=yes \ -Dtests=yes \
@ -731,6 +752,7 @@ rm -f %{buildroot}%{_libdir}/pppd/%{ppp_version}/*.la
rm -f %{buildroot}%{nmplugindir}/*.la rm -f %{buildroot}%{nmplugindir}/*.la
# Don't use the *-initrd.service files yet, wait dracut to support them # Don't use the *-initrd.service files yet, wait dracut to support them
rm -f %{buildroot}%{_systemdgeneratordir}/nm-initrd-generator.sh
rm -f %{buildroot}%{_unitdir}/NetworkManager-config-initrd.service rm -f %{buildroot}%{_unitdir}/NetworkManager-config-initrd.service
rm -f %{buildroot}%{_unitdir}/NetworkManager-initrd.service rm -f %{buildroot}%{_unitdir}/NetworkManager-initrd.service
rm -f %{buildroot}%{_unitdir}/NetworkManager-wait-online-initrd.service rm -f %{buildroot}%{_unitdir}/NetworkManager-wait-online-initrd.service
@ -896,6 +918,9 @@ fi
%{_datadir}/dbus-1/system-services/org.freedesktop.nm_dispatcher.service %{_datadir}/dbus-1/system-services/org.freedesktop.nm_dispatcher.service
%{_datadir}/dbus-1/system-services/org.freedesktop.nm_priv_helper.service %{_datadir}/dbus-1/system-services/org.freedesktop.nm_priv_helper.service
%{_datadir}/polkit-1/actions/*.policy %{_datadir}/polkit-1/actions/*.policy
%if %{with polkit_noauth_group}
%{_datadir}/polkit-1/rules.d/org.freedesktop.NetworkManager.rules
%endif
%{_prefix}/lib/udev/rules.d/*.rules %{_prefix}/lib/udev/rules.d/*.rules
%{_prefix}/lib/firewalld/zones/nm-shared.xml %{_prefix}/lib/firewalld/zones/nm-shared.xml
# systemd stuff # systemd stuff

View file

@ -173,6 +173,7 @@ P_WIFI="${WIFI-1}"
P_WWAN="${WWAN-1}" P_WWAN="${WWAN-1}"
P_TEAM="${TEAM-1}" P_TEAM="${TEAM-1}"
P_BLUETOOTH="${BLUETOOTH-1}" P_BLUETOOTH="${BLUETOOTH-1}"
P_IFCFG_RH="${IFCFG_RH-0}"
P_NMTUI="${NMTUI-1}" P_NMTUI="${NMTUI-1}"
P_NM_CLOUD_SETUP="${NM_CLOUD_SETUP-1}" P_NM_CLOUD_SETUP="${NM_CLOUD_SETUP-1}"
P_OVS="${OVS-1}" P_OVS="${OVS-1}"
@ -202,7 +203,7 @@ if [ -z "$P_FEDORA" -a -z "$P_RHEL" ] ; then
P_FEDORA="$x" P_FEDORA="$x"
P_RHEL=0 P_RHEL=0
else else
x="$(grep -q "ID=fedora" /etc/os-release && sed -n 's/VERSION_ID=//p' /etc/os-release)" x="$(grep -q 'ID="rhel"' /etc/os-release && sed -n 's/^VERSION_ID="*\([0-9]*\).*/\1/p' /etc/os-release)"
if test "$x" -gt 0 ; then if test "$x" -gt 0 ; then
P_FEDORA=0 P_FEDORA=0
P_RHEL="$x" P_RHEL="$x"
@ -293,6 +294,14 @@ if [ -z "$P_MODEM_MANAGER_1" ] ; then
fi fi
fi fi
if [ -z "$TEAM" ] && [ "${P_RHEL-0}" -ge 10 ] ; then
P_TEAM=0
fi
if [ -z "$IFCFG_RH" ] && [ -n "$P_RHEL" ] && [ "$P_RHEL" -le 9 ] ; then
P_IFCFG_RH=1
fi
if bool "$P_DEBUG" ; then if bool "$P_DEBUG" ; then
P_CFLAGS="-g -Og -fexceptions${P_CFLAGS:+ }$P_CFLAGS" P_CFLAGS="-g -Og -fexceptions${P_CFLAGS:+ }$P_CFLAGS"
else else
@ -378,7 +387,7 @@ meson setup\
-Db_lto="$(bool_true "$P_LTO")" \ -Db_lto="$(bool_true "$P_LTO")" \
-Dlibaudit=yes-disabled-by-default \ -Dlibaudit=yes-disabled-by-default \
-Dmodem_manager="$(bool_true "$P_MODEM_MANAGER_1")" \ -Dmodem_manager="$(bool_true "$P_MODEM_MANAGER_1")" \
$(args_enable "$P_WIFI" -Dwifi=true -Dwext="$(bool_true "$P_FEDORA")") \ $(args_enable "$P_WIFI" -Dwifi=true -Dwext=false) \
$(args_enable "$(bool_not_true "$P_WIFI")" -Dwifi=false ) \ $(args_enable "$(bool_not_true "$P_WIFI")" -Dwifi=false ) \
-Diwd="$(bool_true "$P_IWD")" \ -Diwd="$(bool_true "$P_IWD")" \
-Dbluez5_dun="$(bool_true "$P_BLUETOOTH")" \ -Dbluez5_dun="$(bool_true "$P_BLUETOOTH")" \
@ -392,17 +401,17 @@ meson setup\
-Dselinux=true \ -Dselinux=true \
-Dpolkit=true \ -Dpolkit=true \
-Dconfig_auth_polkit_default=true \ -Dconfig_auth_polkit_default=true \
-Dmodify_system=true \
-Dconcheck=true \ -Dconcheck=true \
-Dlibpsl="$(bool_true "$P_FEDORA")" \ -Dlibpsl="$(bool_true "$P_FEDORA")" \
-Dsession_tracking=systemd \ -Dsession_tracking=systemd \
-Dsuspend_resume=systemd \ -Dsuspend_resume=systemd \
-Dsystemdsystemunitdir=/usr/lib/systemd/system \ -Dsystemdsystemunitdir=/usr/lib/systemd/system \
-Dsystemdsystemgeneratordir=/usr/lib/systemd/system-generators \
-Dsystem_ca_path=/etc/pki/tls/cert.pem \ -Dsystem_ca_path=/etc/pki/tls/cert.pem \
-Ddbus_conf_dir="$P_DBUS_SYS_DIR" \ -Ddbus_conf_dir="$P_DBUS_SYS_DIR" \
-Dtests=yes \ -Dtests=yes \
-Dvalgrind=no \ -Dvalgrind=no \
-Difcfg_rh=true \ -Difcfg_rh="$(bool_true "$P_IFCFG_RH")" \
-Difupdown=false \ -Difupdown=false \
$(args_enable "$P_PPP" -Dppp=true -Dpppd="$D_SBINDIR/pppd" -Dpppd_plugin_dir="$D_LIBDIR/pppd/$P_PPP_VERSION") \ $(args_enable "$P_PPP" -Dppp=true -Dpppd="$D_SBINDIR/pppd" -Dpppd_plugin_dir="$D_LIBDIR/pppd/$P_PPP_VERSION") \
$(args_enable "$(bool_not_true "$P_PPP")" -Dppp=false ) \ $(args_enable "$(bool_not_true "$P_PPP")" -Dppp=false ) \

View file

@ -169,6 +169,7 @@ meson setup build \
-D ld_gc=false \ -D ld_gc=false \
-D session_tracking=no \ -D session_tracking=no \
-D systemdsystemunitdir=no \ -D systemdsystemunitdir=no \
-D systemdsystemgeneratordir=no \
-D systemd_journal=false \ -D systemd_journal=false \
-D selinux=false \ -D selinux=false \
-D libaudit=no \ -D libaudit=no \

View file

@ -1,10 +1,10 @@
[Unit] [Unit]
Description=NetworkManager Configuration (initrd) Description=NetworkManager Configuration (initrd)
AssertPathExists=/etc/initrd-release
DefaultDependencies=no DefaultDependencies=no
Wants=systemd-journald.socket Wants=systemd-journald.socket
After=systemd-journald.socket After=systemd-journald.socket
Before=systemd-udevd.service systemd-udev-trigger.service Before=systemd-udevd.service systemd-udev-trigger.service
ConditionPathExists=/etc/initrd-release
[Service] [Service]
Type=oneshot Type=oneshot
@ -22,6 +22,3 @@ ExecStartPost=/bin/sh -c ' \
fi \ fi \
' '
RemainAfterExit=yes RemainAfterExit=yes
[Install]
WantedBy=initrd.target

View file

@ -1,11 +1,11 @@
[Unit] [Unit]
Description=NetworkManager (initrd) Description=NetworkManager (initrd)
AssertPathExists=/etc/initrd-release
DefaultDependencies=no DefaultDependencies=no
Wants=systemd-udev-trigger.service network.target Wants=systemd-udev-trigger.service network.target
After=systemd-udev-trigger.service network-pre.target dbus.service NetworkManager-config-initrd.service After=systemd-udev-trigger.service network-pre.target dbus.service NetworkManager-config-initrd.service
Before=network.target Before=network.target
BindsTo=dbus.service BindsTo=dbus.service
ConditionPathExists=/etc/initrd-release
ConditionPathExists=/run/NetworkManager/initrd/neednet ConditionPathExists=/run/NetworkManager/initrd/neednet
ConditionPathExistsGlob=|/usr/lib/NetworkManager/system-connections/* ConditionPathExistsGlob=|/usr/lib/NetworkManager/system-connections/*
ConditionPathExistsGlob=|/run/NetworkManager/system-connections/* ConditionPathExistsGlob=|/run/NetworkManager/system-connections/*
@ -22,11 +22,3 @@ Environment=NM_CONFIG_ENABLE_TAG=initrd
Restart=on-failure Restart=on-failure
ProtectSystem=true ProtectSystem=true
ProtectHome=read-only ProtectHome=read-only
[Install]
WantedBy=initrd.target
# We want to enable NetworkManager-wait-online-initrd.service whenever this
# service is enabled. NetworkManager-wait-online-initrd.service has
# WantedBy=network-online.target, so enabling it only has an effect if
# network-online.target itself is enabled or pulled in by some other unit.
Also=NetworkManager-config-initrd.service NetworkManager-wait-online-initrd.service

View file

@ -1,10 +1,10 @@
[Unit] [Unit]
Description=NetworkManager Wait Online (initrd) Description=NetworkManager Wait Online (initrd)
AssertPathExists=/etc/initrd-release
DefaultDependencies=no DefaultDependencies=no
Requires=NetworkManager-initrd.service Requires=NetworkManager-initrd.service
After=NetworkManager-initrd.service After=NetworkManager-initrd.service
Before=network-online.target Before=network-online.target
ConditionPathExists=/etc/initrd-release
ConditionPathExists=/run/NetworkManager/initrd/neednet ConditionPathExists=/run/NetworkManager/initrd/neednet
[Service] [Service]
@ -21,6 +21,3 @@ Type=oneshot
ExecStart=@bindir@/nm-online -s -q ExecStart=@bindir@/nm-online -s -q
RemainAfterExit=yes RemainAfterExit=yes
Environment=NM_ONLINE_TIMEOUT=3600 Environment=NM_ONLINE_TIMEOUT=3600
[Install]
WantedBy=initrd.target network-online.target

View file

@ -21,8 +21,16 @@ TimeoutStartSec=600
CapabilityBoundingSet=CAP_NET_ADMIN CAP_DAC_OVERRIDE CAP_NET_RAW CAP_BPF CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_MODULE CAP_AUDIT_WRITE CAP_KILL CAP_SYS_CHROOT CapabilityBoundingSet=CAP_NET_ADMIN CAP_DAC_OVERRIDE CAP_NET_RAW CAP_BPF CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_MODULE CAP_AUDIT_WRITE CAP_KILL CAP_SYS_CHROOT
ProtectSystem=true PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=read-only ProtectHome=read-only
ProtectKernelLogs=true
ProtectSystem=true
RestrictRealtime=true
RestrictSUIDSGID=true
# We require file descriptors for DHCP etc. When activating many interfaces, # We require file descriptors for DHCP etc. When activating many interfaces,
# the default limit of 1024 is easily reached. # the default limit of 1024 is easily reached.

View file

@ -55,21 +55,22 @@ if install_udevdir
endif endif
if enable_polkit if enable_polkit
policy = 'org.freedesktop.NetworkManager.policy'
policy_in = configure_file(
input: policy + '.in.in',
output: '@BASENAME@',
configuration: data_conf,
)
i18n.merge_file( i18n.merge_file(
input: policy_in, input: 'org.freedesktop.NetworkManager.policy.in',
output: '@BASENAME@', output: '@BASENAME@',
po_dir: po_dir, po_dir: po_dir,
install: true, install: true,
install_dir: polkit_gobject_policydir, install_dir: polkit_policydir,
) )
if polkit_noauth_group != ''
configure_file(
input: 'org.freedesktop.NetworkManager.rules.in',
output: '@BASENAME@',
install_dir: polkit_rulesdir,
configuration: {'NM_POLKIT_NOAUTH_GROUP': polkit_noauth_group},
)
endif
endif endif
if enable_firewalld_zone if enable_firewalld_zone

View file

@ -117,8 +117,8 @@
<message>System policy prevents modification of network settings for all users</message> <message>System policy prevents modification of network settings for all users</message>
<defaults> <defaults>
<allow_any>auth_admin_keep</allow_any> <allow_any>auth_admin_keep</allow_any>
<allow_inactive>@NM_MODIFY_SYSTEM_POLICY@</allow_inactive> <allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>@NM_MODIFY_SYSTEM_POLICY@</allow_active> <allow_active>auth_admin_keep</allow_active>
</defaults> </defaults>
</action> </action>

View file

@ -0,0 +1,17 @@
// NetworkManager authorizations/policy for the @NM_POLKIT_NOAUTH_GROUP@ group.
//
// DO NOT EDIT THIS FILE, it will be overwritten on update.
//
// Allow users in the @NM_POLKIT_NOAUTH_GROUP@ group to create system-wide connections without being
// prompted for a password if they are in a local console.
// This is optional and is only recommended to maintain backwards compatibility
// in systems where it was already working in this way. It is discouraged
// otherwise.
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.NetworkManager.settings.modify.system" &&
subject.isInGroup("@NM_POLKIT_NOAUTH_GROUP@") &&
subject.local) {
return polkit.Result.YES;
}
});

View file

@ -183,6 +183,7 @@
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Bridge.xml"/> <xi:include href="dbus-org.freedesktop.NetworkManager.Device.Bridge.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Dummy.xml"/> <xi:include href="dbus-org.freedesktop.NetworkManager.Device.Dummy.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Generic.xml"/> <xi:include href="dbus-org.freedesktop.NetworkManager.Device.Generic.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Geneve.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Hsr.xml"/> <xi:include href="dbus-org.freedesktop.NetworkManager.Device.Hsr.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.IPTunnel.xml"/> <xi:include href="dbus-org.freedesktop.NetworkManager.Device.IPTunnel.xml"/>
<xi:include href="dbus-org.freedesktop.NetworkManager.Device.Infiniband.xml"/> <xi:include href="dbus-org.freedesktop.NetworkManager.Device.Infiniband.xml"/>

View file

@ -317,6 +317,7 @@ print ("NetworkManager version " + client.get_version())]]></programlisting></in
<xi:include href="xml/nm-setting-dummy.xml"/> <xi:include href="xml/nm-setting-dummy.xml"/>
<xi:include href="xml/nm-setting-ethtool.xml"/> <xi:include href="xml/nm-setting-ethtool.xml"/>
<xi:include href="xml/nm-setting-generic.xml"/> <xi:include href="xml/nm-setting-generic.xml"/>
<xi:include href="xml/nm-setting-geneve.xml"/>
<xi:include href="xml/nm-setting-gsm.xml"/> <xi:include href="xml/nm-setting-gsm.xml"/>
<xi:include href="xml/nm-setting-hostname.xml"/> <xi:include href="xml/nm-setting-hostname.xml"/>
<xi:include href="xml/nm-setting-hsr.xml"/> <xi:include href="xml/nm-setting-hsr.xml"/>
@ -377,6 +378,7 @@ print ("NetworkManager version " + client.get_version())]]></programlisting></in
<xi:include href="xml/nm-device-dummy.xml"/> <xi:include href="xml/nm-device-dummy.xml"/>
<xi:include href="xml/nm-device-ethernet.xml"/> <xi:include href="xml/nm-device-ethernet.xml"/>
<xi:include href="xml/nm-device-generic.xml"/> <xi:include href="xml/nm-device-generic.xml"/>
<xi:include href="xml/nm-device-geneve.xml"/>
<xi:include href="xml/nm-device-hsr.xml"/> <xi:include href="xml/nm-device-hsr.xml"/>
<xi:include href="xml/nm-device-infiniband.xml"/> <xi:include href="xml/nm-device-infiniband.xml"/>
<xi:include href="xml/nm-device-ip-tunnel.xml"/> <xi:include href="xml/nm-device-ip-tunnel.xml"/>

View file

@ -15,6 +15,7 @@ ifaces = [
'org.freedesktop.NetworkManager.Device.Bridge', 'org.freedesktop.NetworkManager.Device.Bridge',
'org.freedesktop.NetworkManager.Device.Dummy', 'org.freedesktop.NetworkManager.Device.Dummy',
'org.freedesktop.NetworkManager.Device.Generic', 'org.freedesktop.NetworkManager.Device.Generic',
'org.freedesktop.NetworkManager.Device.Geneve',
'org.freedesktop.NetworkManager.Device.Hsr', 'org.freedesktop.NetworkManager.Device.Hsr',
'org.freedesktop.NetworkManager.Device.IPTunnel', 'org.freedesktop.NetworkManager.Device.IPTunnel',
'org.freedesktop.NetworkManager.Device.Infiniband', 'org.freedesktop.NetworkManager.Device.Infiniband',

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/">
<!--
org.freedesktop.NetworkManager.Device.Geneve:
@short_description: GENEVE Device.
-->
<interface name="org.freedesktop.NetworkManager.Device.Geneve">
<!--
Id:
@since: 1.58
The GENEVE Virtual Network Identifier (VNI).
-->
<property name="Id" type="u" access="read"/>
<!--
Remote:
@since: 1.58
The IP (v4 or v6) address of the remote endpoint to which GENEVE packets
are sent.
-->
<property name="Remote" type="s" access="read"/>
<!--
Tos:
@since: 1.58
The value to use in the IP ToS field for GENEVE packets sent to the remote
endpoint.
-->
<property name="Tos" type="y" access="read"/>
<!--
Ttl:
@since: 1.58
The value to use in the IP TTL field for GENEVE packets sent to the remote
endpoint.
-->
<property name="Ttl" type="i" access="read"/>
<!--
Df:
@since: 1.58
The Don't Fragment (DF) flag setting for GENEVE packets. 0 means unset,
1 means set, 2 means inherit from the underlying interface.
-->
<property name="Df" type="y" access="read"/>
<!--
DstPort:
@since: 1.58
Destination port for outgoing GENEVE packets.
-->
<property name="DstPort" type="q" access="read"/>
</interface>
</node>

View file

@ -175,6 +175,9 @@
property has a similar effect to configuring the device as unmanaged via property has a similar effect to configuring the device as unmanaged via
the keyfile.unmanaged-devices setting in NetworkManager.conf. Changes to the keyfile.unmanaged-devices setting in NetworkManager.conf. Changes to
this value are not persistent and lost after NetworkManager restart. this value are not persistent and lost after NetworkManager restart.
DEPRECATED: 1.58: Use the SetManaged method instead, which supports
additional features like persisting the state to disk
--> -->
<property name="Managed" type="b" access="readwrite"/> <property name="Managed" type="b" access="readwrite"/>
@ -391,6 +394,20 @@
--> -->
<method name="Delete"/> <method name="Delete"/>
<!--
SetManaged:
@managed:(<link linkend="NMDeviceManaged">NMDeviceManaged</link>) Whether the device is managed. Possible values are "no" (0), "yes" (1) and "reset" (2).
@flags: (<link linkend="NMDeviceManagedFlags">NMDeviceManagedFlags</link>) flags.
@since: 1.58
Set the managed state of the device. With the flags argument different
behaviors can be achieved, like storing the new managed state to disk.
-->
<method name="SetManaged">
<arg name="managed" type="u" direction="in"/>
<arg name="flags" type="u" direction="in"/>
</method>
<!-- <!--
StateChanged: StateChanged:
@new_state: (<link linkend="NMDeviceState">NMDeviceState</link>) The new state of the device. @new_state: (<link linkend="NMDeviceState">NMDeviceState</link>) The new state of the device.

View file

@ -62,7 +62,7 @@
<!-- <!--
GetSecrets: GetSecrets:
@setting_name: Name of the setting to return secrets for. If empty, all secrets will be returned. @setting_name: Name of the setting to return secrets for (mandatory).
@secrets: Nested settings maps containing secrets. @secrets: Nested settings maps containing secrets.
Get the secrets belonging to this network configuration. Only secrets from Get the secrets belonging to this network configuration. Only secrets from

View file

@ -83,6 +83,11 @@
note that your distribution or other packages may drop configuration snippets for NetworkManager, such note that your distribution or other packages may drop configuration snippets for NetworkManager, such
that they are part of the factory default. that they are part of the factory default.
</para> </para>
<para>
The options that are indicated as boolean can be set to one of these values:
<literal>yes</literal>, <literal>true</literal>, <literal>on</literal>, <literal>1</literal>,
<literal>no</literal>, <literal>false</literal>, <literal>off</literal>, <literal>0</literal>.
</para>
</refsect1> </refsect1>
@ -949,6 +954,10 @@ ipv6.ip6-privacy=0
<term><varname>ipv4.forwarding</varname></term> <term><varname>ipv4.forwarding</varname></term>
<listitem><para>Whether to configure IPv4 sysctl interface-specific forwarding. When enabled, the interface will act as a router to forward the IPv4 packet from one interface to another. If left unspecified, "auto" is used, so NetworkManager sets the IPv4 forwarding if any shared connection is active, or it will use the kernel default value otherwise. The "ipv4.forwarding" property is ignored when "ipv4.method" is set to "shared", because forwarding is always enabled in this case. The accepted values are: 0: disabled, 1: enabled, 2: auto, 3: ignored (leave the forwarding unchanged).</para></listitem> <listitem><para>Whether to configure IPv4 sysctl interface-specific forwarding. When enabled, the interface will act as a router to forward the IPv4 packet from one interface to another. If left unspecified, "auto" is used, so NetworkManager sets the IPv4 forwarding if any shared connection is active, or it will use the kernel default value otherwise. The "ipv4.forwarding" property is ignored when "ipv4.method" is set to "shared", because forwarding is always enabled in this case. The accepted values are: 0: disabled, 1: enabled, 2: auto, 3: ignored (leave the forwarding unchanged).</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>ipv4.clat</varname></term>
<listitem><para>If left unspecified, defaults to "no".</para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>ipv4.routed-dns</varname></term> <term><varname>ipv4.routed-dns</varname></term>
</varlistentry> </varlistentry>
@ -967,7 +976,7 @@ ipv6.ip6-privacy=0
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>ipv4.dhcp-ipv6-only-preferred</varname></term> <term><varname>ipv4.dhcp-ipv6-only-preferred</varname></term>
<listitem><para>If left unspecified, the "IPv6-only preferred" DHCPv4 option is disabled.</para></listitem> <listitem><para>If left unspecified, it defaults to "auto".</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>ipv4.dhcp-hostname-flags</varname></term> <term><varname>ipv4.dhcp-hostname-flags</varname></term>
@ -1249,12 +1258,13 @@ managed=1
<term><varname>managed</varname></term> <term><varname>managed</varname></term>
<listitem> <listitem>
<para> <para>
Whether the device is managed or not. A device can be A boolean value specifying whether the device is
marked as managed via udev rules (ENV{NM_UNMANAGED}), managed or not. A device can be marked as managed via
or via setting plugins (keyfile.unmanaged-devices). udev rules (ENV{NM_UNMANAGED}), or via setting plugins
This is yet another way. Note that this configuration (keyfile.unmanaged-devices). This is yet another
can be overruled at runtime via D-Bus. Also, it has way. Note that this configuration can be overruled at
higher priority then udev rules. runtime via D-Bus. Also, it has higher priority than
udev rules.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -1323,9 +1333,27 @@ managed=1
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="keep-configuration"> <varlistentry id="check-connectivity">
<term><varname>keep-configuration</varname></term> <term><varname>check-connectivity</varname></term>
<listitem> <listitem>
<para>
A boolean value specifying whether NetworkManager will perform a connectivity check
for this device. Defaults to <literal>yes</literal>.
</para>
<para>
This setting does nothing if the connectivity check has been
disabled globally using the
<literal>connectivity.enabled</literal> setting.
</para>
</listitem>
</varlistentry>
<varlistentry id="keep-configuration">
<term><varname>keep-configuration</varname></term>
<listitem>
<para>
A boolean value indicating whether the existing device
configuration is kept at startup.
</para>
<para> <para>
On startup, NetworkManager tries to not interfere with On startup, NetworkManager tries to not interfere with
interfaces that are already configured. It does so by interfaces that are already configured. It does so by
@ -1422,16 +1450,16 @@ managed=1
<term><varname>wifi.iwd.autoconnect</varname></term> <term><varname>wifi.iwd.autoconnect</varname></term>
<listitem> <listitem>
<para> <para>
If <literal>wifi.backend</literal> is <literal>iwd</literal>, setting this to A boolean value. If <literal>wifi.backend</literal> is <literal>iwd</literal>,
<literal>false</literal> forces IWD's autoconnect mechanism to be disabled for setting this to <literal>false</literal> forces IWD's autoconnect mechanism to be
this device and connections will only be initiated by NetworkManager whether disabled for this device and connections will only be initiated by NetworkManager
commanded by a client or automatically. Leaving it <literal>true</literal> (default) whether commanded by a client or automatically. Leaving it <literal>true</literal>
stops NetworkManager from automatically initiating connections and allows (default) stops NetworkManager from automatically initiating connections and allows
IWD to use its network ranking and scanning logic to decide the best networks IWD to use its network ranking and scanning logic to decide the best networks to
to autoconnect to next. Connections' <literal>autoconnect-priority</literal>, autoconnect to next. Connections' <literal>autoconnect-priority</literal>,
<literal>autoconnect-retries</literal> settings will be ignored. Other settings <literal>autoconnect-retries</literal> settings will be ignored. Other settings like
like <literal>permissions</literal> or <literal>multi-connect</literal> may interfere <literal>permissions</literal> or <literal>multi-connect</literal> may interfere with
with IWD connection attempts. IWD connection attempts.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -1490,7 +1518,7 @@ managed=1
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><varname>enabled</varname></term> <term><varname>enabled</varname></term>
<listitem><para>Whether connectivity check is enabled. <listitem><para>A boolean indicating whether connectivity check is enabled.
Note that to enable connectivity check, a valid uri must Note that to enable connectivity check, a valid uri must
also be configured. The value defaults to true, but since also be configured. The value defaults to true, but since
the uri is unset by default, connectivity check may be disabled. the uri is unset by default, connectivity check may be disabled.

View file

@ -1436,15 +1436,31 @@
</arg> </arg>
<arg> <arg>
<option>managed</option> <option>managed</option>
<group>
<arg choice='plain'>--permanent</arg>
<arg choice='plain'>--permanent-only</arg>
</group>
<group choice='req'> <group choice='req'>
<arg choice='plain'>yes</arg> <arg choice='plain'>yes</arg>
<arg choice='plain'>no</arg> <arg choice='plain'>no</arg>
<arg choice='plain'>up</arg>
<arg choice='plain'>down</arg>
<arg choice='plain'>reset</arg>
</group> </group>
</arg> </arg>
</term> </term>
<listitem> <listitem>
<para>Set device properties.</para> <para>Set device properties.</para>
<para>The <option>managed</option> property accepts a <option>--permanent</option>
option to persist the managed state to disk, and not only in runtime. With
<option>--permanent-only</option> only the permanent managed state is set, and not the
runtime managed state. The special values <option>up</option> and <option>down</option>
can be used to set the administrative state of the device at the same time as the runtime
managed state. The <option>reset</option> value clears the explicit managed setting, and
with <option>--permanent</option> or <option>--permanent-only</option> it also removes
the persisted managed setting.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -1718,6 +1734,7 @@
<group choice='req'> <group choice='req'>
<arg choice='plain'>a</arg> <arg choice='plain'>a</arg>
<arg choice='plain'>bg</arg> <arg choice='plain'>bg</arg>
<arg choice='plain'>6GHz</arg>
</group> </group>
</arg> </arg>
<arg><option>channel</option> <replaceable>channel</replaceable></arg> <arg><option>channel</option> <replaceable>channel</replaceable></arg>
@ -1851,9 +1868,9 @@
connections with an option of restoring the network configuration to a connections with an option of restoring the network configuration to a
known good state in case of an error.</para> known good state in case of an error.</para>
<para>If the a list of interface names is specified, the checkpoint is <para>If a list of interface names is specified, the checkpoint is
taken, the checkpoint is takes only on the specified devices. Otherwise taken only on the specified devices. Otherwise a checkpoint is taken for
a checkpoint is taken for all devices.</para> all devices.</para>
<para>Currently the timeout defaults to 15 seconds. This may change in <para>Currently the timeout defaults to 15 seconds. This may change in
a future version.</para> a future version.</para>

View file

@ -5,7 +5,7 @@ project(
# NOTE: When incrementing version also add corresponding # NOTE: When incrementing version also add corresponding
# NM_VERSION_x_y_z macros in # NM_VERSION_x_y_z macros in
# "src/libnm-core-public/nm-version-macros.h.in" # "src/libnm-core-public/nm-version-macros.h.in"
version: '1.56.0', version: '1.57.3-dev',
license: 'GPL2+', license: 'GPL2+',
default_options: [ default_options: [
'buildtype=debugoptimized', 'buildtype=debugoptimized',
@ -358,7 +358,11 @@ enable_iwd = get_option('iwd')
assert((not enable_iwd) or enable_wifi, 'Enabling iwd support requires Wi-Fi support as well') assert((not enable_iwd) or enable_wifi, 'Enabling iwd support requires Wi-Fi support as well')
config_h.set10('WITH_IWD', enable_iwd) config_h.set10('WITH_IWD', enable_iwd)
enable_wext = get_option('wext') wext = get_option('wext')
if wext == 'true'
error('Wireless Extensions support is deprecated and will be removed in the future. Use -Dwext=force to keep using it')
endif
enable_wext = (wext == 'force')
config_h.set10('HAVE_WEXT', enable_wext) config_h.set10('HAVE_WEXT', enable_wext)
# Checks for libdl - on certain platforms its part of libc # Checks for libdl - on certain platforms its part of libc
@ -408,6 +412,14 @@ if install_systemdunitdir and systemd_systemdsystemunitdir == ''
systemd_systemdsystemunitdir = systemd_dep.get_variable(pkgconfig: 'systemdsystemunitdir', pkgconfig_define: ['rootprefix', nm_prefix]) systemd_systemdsystemunitdir = systemd_dep.get_variable(pkgconfig: 'systemdsystemunitdir', pkgconfig_define: ['rootprefix', nm_prefix])
endif endif
systemd_systemdsystemgeneratordir = get_option('systemdsystemgeneratordir')
install_systemdgeneratordir = (systemd_systemdsystemgeneratordir != 'no')
if install_systemdgeneratordir and systemd_systemdsystemgeneratordir == ''
assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user generator dir or disable it')
systemd_systemdsystemgeneratordir = systemd_dep.get_variable(pkgconfig: 'systemdsystemgeneratordir', pkgconfig_define: ['rootprefix', nm_prefix])
endif
enable_systemd_journal = get_option('systemd_journal') enable_systemd_journal = get_option('systemd_journal')
if enable_systemd_journal if enable_systemd_journal
assert(libsystemd_dep.found(), 'Missing systemd-journald support') assert(libsystemd_dep.found(), 'Missing systemd-journald support')
@ -502,6 +514,16 @@ if enable_selinux
endif endif
config_h.set10('HAVE_SELINUX', enable_selinux) config_h.set10('HAVE_SELINUX', enable_selinux)
# CLAT support
enable_clat = get_option('clat')
if enable_clat
libbpf = dependency('libbpf', version: '>= 0.1.0', required: false)
assert(libbpf.found(), 'You must have libbpf installed to build. Use -Dclat=false to disable use of it')
libndp_dep = dependency('libndp', version: '>= 1.9', required: false)
assert(libndp_dep.found(), 'You must have libndp >= 1.9 installed to build with CLAT support. Use -Dclat=false to disable it')
endif
config_h.set10('HAVE_CLAT', enable_clat)
# libaudit support # libaudit support
libaudit = get_option('libaudit') libaudit = get_option('libaudit')
enable_libaudit = libaudit.contains('yes') enable_libaudit = libaudit.contains('yes')
@ -526,7 +548,8 @@ config_h.set10('WITH_TEAMDCTL', enable_teamdctl)
enable_polkit = get_option('polkit') enable_polkit = get_option('polkit')
if enable_polkit if enable_polkit
# FIXME: policydir should be relative to `datadir`, not `prefix`. Fixed in https://gitlab.freedesktop.org/polkit/polkit/merge_requests/2 # FIXME: policydir should be relative to `datadir`, not `prefix`. Fixed in https://gitlab.freedesktop.org/polkit/polkit/merge_requests/2
polkit_gobject_policydir = dependency('polkit-gobject-1').get_variable(pkgconfig: 'policydir', pkgconfig_define: ['prefix', nm_prefix]) polkit_policydir = dependency('polkit-gobject-1').get_variable(pkgconfig: 'policydir', pkgconfig_define: ['prefix', nm_prefix])
polkit_rulesdir = join_paths(fs.parent(polkit_policydir), 'rules.d')
endif endif
config_auth_polkit_default = get_option('config_auth_polkit_default') config_auth_polkit_default = get_option('config_auth_polkit_default')
@ -536,6 +559,12 @@ endif
config_h.set_quoted('NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT', config_auth_polkit_default) config_h.set_quoted('NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT', config_auth_polkit_default)
enable_modify_system = get_option('modify_system') enable_modify_system = get_option('modify_system')
if enable_modify_system
# FIXME: remove this after everyone has stopped using modify_system
error('modify_system=true is no longer allowed due to security reasons')
endif
polkit_noauth_group = get_option('polkit_noauth_group')
polkit_agent_helper_1_path = get_option('polkit_agent_helper_1') polkit_agent_helper_1_path = get_option('polkit_agent_helper_1')
foreach p : [ '/usr/libexec/polkit-agent-helper-1', foreach p : [ '/usr/libexec/polkit-agent-helper-1',
@ -968,7 +997,6 @@ data_conf.set('NM_DHCP_CLIENTS_ENABLED', ', '.join(config_dhcp_c
data_conf.set('NM_MAJOR_VERSION', nm_major_version) data_conf.set('NM_MAJOR_VERSION', nm_major_version)
data_conf.set('NM_MICRO_VERSION', nm_micro_version) data_conf.set('NM_MICRO_VERSION', nm_micro_version)
data_conf.set('NM_MINOR_VERSION', nm_minor_version) data_conf.set('NM_MINOR_VERSION', nm_minor_version)
data_conf.set('NM_MODIFY_SYSTEM_POLICY', (enable_modify_system ? 'yes' : 'auth_admin_keep'))
data_conf.set('NM_VERSION', nm_version) data_conf.set('NM_VERSION', nm_version)
data_conf.set('VERSION', nm_version) data_conf.set('VERSION', nm_version)
data_conf.set('bindir', nm_bindir) data_conf.set('bindir', nm_bindir)
@ -1084,6 +1112,7 @@ output = '\nSystem paths:\n'
output += ' prefix: ' + nm_prefix + '\n' output += ' prefix: ' + nm_prefix + '\n'
output += ' exec_prefix: ' + nm_prefix + '\n' output += ' exec_prefix: ' + nm_prefix + '\n'
output += ' systemdunitdir: ' + systemd_systemdsystemunitdir + '\n' output += ' systemdunitdir: ' + systemd_systemdsystemunitdir + '\n'
output += ' systemdgeneratordir: ' + systemd_systemdsystemgeneratordir + '\n'
output += ' udev_dir: ' + udev_udevdir + '\n' output += ' udev_dir: ' + udev_udevdir + '\n'
output += ' nmbinary: ' + nm_pkgsbindir + '\n' output += ' nmbinary: ' + nm_pkgsbindir + '\n'
output += ' nmconfdir: ' + nm_pkgconfdir + '\n' output += ' nmconfdir: ' + nm_pkgconfdir + '\n'
@ -1098,17 +1127,7 @@ output += ' dbus_conf_dir: ' + dbus_conf_dir + '\n'
output += '\nPlatform:\n' output += '\nPlatform:\n'
output += ' session tracking: ' + ','.join(session_trackers) + '\n' output += ' session tracking: ' + ','.join(session_trackers) + '\n'
output += ' suspend/resume: ' + suspend_resume + '\n' output += ' suspend/resume: ' + suspend_resume + '\n'
output += ' policykit: ' + enable_polkit.to_string() + ' (default: ' + config_auth_polkit_default + ')' output += ' policykit: ' + enable_polkit.to_string() + ' (default: ' + config_auth_polkit_default + ', noauth_group: "' + polkit_noauth_group + '")\n'
if enable_polkit
output += ' ('
if enable_modify_system
output += 'permissive'
else
output += 'restrictive'
endif
output += ' modify.system)'
endif
output += '\n'
output += ' polkit-agent-helper-1: ' + polkit_agent_helper_1_path + '\n' output += ' polkit-agent-helper-1: ' + polkit_agent_helper_1_path + '\n'
output += ' selinux: ' + enable_selinux.to_string() + '\n' output += ' selinux: ' + enable_selinux.to_string() + '\n'
output += ' systemd-journald: ' + enable_systemd_journal.to_string() + ' (default: logging.backend=' + config_logging_backend_default + ')\n' output += ' systemd-journald: ' + enable_systemd_journal.to_string() + ' (default: logging.backend=' + config_logging_backend_default + ')\n'
@ -1136,6 +1155,7 @@ output += ' ofono: ' + enable_ofono.to_string() + '\n'
output += ' concheck: ' + enable_concheck.to_string() + '\n' output += ' concheck: ' + enable_concheck.to_string() + '\n'
output += ' libteamdctl: ' + enable_teamdctl.to_string() + '\n' output += ' libteamdctl: ' + enable_teamdctl.to_string() + '\n'
output += ' ovs: ' + enable_ovs.to_string() + '\n' output += ' ovs: ' + enable_ovs.to_string() + '\n'
output += ' clat: ' + enable_clat.to_string() + '\n'
output += ' nmcli: ' + enable_nmcli.to_string() + '\n' output += ' nmcli: ' + enable_nmcli.to_string() + '\n'
output += ' nmtui: ' + enable_nmtui.to_string() + '\n' output += ' nmtui: ' + enable_nmtui.to_string() + '\n'
output += ' nm-cloud-setup: ' + enable_nm_cloud_setup.to_string() + '\n' output += ' nm-cloud-setup: ' + enable_nm_cloud_setup.to_string() + '\n'

View file

@ -1,5 +1,6 @@
# system paths # system paths
option('systemdsystemunitdir', type: 'string', value: '', description: 'Directory for systemd service files') option('systemdsystemunitdir', type: 'string', value: '', description: 'Directory for systemd service files')
option('systemdsystemgeneratordir', type: 'string', value: '', description: 'Directory for systemd generator files')
option('system_ca_path', type: 'string', value: '/etc/ssl/certs', description: 'path to system CA certificates') option('system_ca_path', type: 'string', value: '/etc/ssl/certs', description: 'path to system CA certificates')
option('udev_dir', type: 'string', value: '', description: 'Absolute path of the udev base directory. Set to \'no\' not to install the udev rule') option('udev_dir', type: 'string', value: '', description: 'Absolute path of the udev base directory. Set to \'no\' not to install the udev rule')
option('dbus_conf_dir', type: 'string', value: '', description: 'where D-Bus system.d directory is') option('dbus_conf_dir', type: 'string', value: '', description: 'where D-Bus system.d directory is')
@ -18,7 +19,8 @@ option('session_tracking', type: 'combo', choices: ['systemd', 'elogind', 'no'],
option('suspend_resume', type: 'combo', choices: ['systemd', 'elogind', 'consolekit', 'auto'], value: 'auto', description: 'Build NetworkManager with specific suspend/resume support') option('suspend_resume', type: 'combo', choices: ['systemd', 'elogind', 'consolekit', 'auto'], value: 'auto', description: 'Build NetworkManager with specific suspend/resume support')
option('polkit', type: 'boolean', value: true, description: 'User auth-polkit configuration option.') option('polkit', type: 'boolean', value: true, description: 'User auth-polkit configuration option.')
option('config_auth_polkit_default', type: 'combo', choices: ['default', 'true', 'false', 'root-only'], value: 'default', description: 'Default value for configuration main.auth-polkit.') option('config_auth_polkit_default', type: 'combo', choices: ['default', 'true', 'false', 'root-only'], value: 'default', description: 'Default value for configuration main.auth-polkit.')
option('modify_system', type: 'boolean', value: false, description: 'Allow users to modify system connections') option('modify_system', type: 'boolean', value: false, description: 'Allow users to modify system connections (option no longer supported, don\'t use)')
option('polkit_noauth_group', type: 'string', value: '', description: 'Allow users of the selected group, typically sudo or wheel, to modify system connections without introducing a password (discouraged)')
option('polkit_agent_helper_1', type: 'string', value: '', description: 'Path name to the polkit-agent-helper-1 binary from polkit') option('polkit_agent_helper_1', type: 'string', value: '', description: 'Path name to the polkit-agent-helper-1 binary from polkit')
option('selinux', type: 'boolean', value: true, description: 'Build with SELinux') option('selinux', type: 'boolean', value: true, description: 'Build with SELinux')
option('systemd_journal', type: 'boolean', value: true, description: 'Use systemd journal for logging') option('systemd_journal', type: 'boolean', value: true, description: 'Use systemd journal for logging')
@ -28,7 +30,7 @@ option('hostname_persist', type: 'combo', choices: ['default', 'suse', 'gentoo',
option('libaudit', type: 'combo', choices: ['yes', 'yes-disabled-by-default', 'no'], value: 'yes', description: 'Build with audit daemon support. yes-disabled-by-default enables support, but disables it unless explicitly configured via NetworkManager.conf') option('libaudit', type: 'combo', choices: ['yes', 'yes-disabled-by-default', 'no'], value: 'yes', description: 'Build with audit daemon support. yes-disabled-by-default enables support, but disables it unless explicitly configured via NetworkManager.conf')
# features # features
option('wext', type: 'boolean', value: true, description: 'Enable or disable Linux Wireless Extensions') option('wext', type: 'combo', choices: ['true', 'false', 'force' ], value: 'false', description: 'Enable or disable Linux Wireless Extensions (deprecated). wext support will be removed in a future release, don\'t rely on this.')
option('wifi', type: 'boolean', value: true, description: 'enable Wi-Fi support') option('wifi', type: 'boolean', value: true, description: 'enable Wi-Fi support')
option('iwd', type: 'boolean', value: false, description: 'enable iwd support (experimental)') option('iwd', type: 'boolean', value: false, description: 'enable iwd support (experimental)')
option('ppp', type: 'boolean', value: true, description: 'enable PPP/PPPoE support') option('ppp', type: 'boolean', value: true, description: 'enable PPP/PPPoE support')
@ -46,6 +48,9 @@ option('nm_cloud_setup', type: 'boolean', value: true, description: 'Build nm-cl
option('bluez5_dun', type: 'boolean', value: false, description: 'enable Bluez5 DUN support') option('bluez5_dun', type: 'boolean', value: false, description: 'enable Bluez5 DUN support')
option('ebpf', type: 'combo', choices: ['auto', 'true', 'false'], description: 'Enable eBPF support (deprecated)') option('ebpf', type: 'combo', choices: ['auto', 'true', 'false'], description: 'Enable eBPF support (deprecated)')
option('nbft', type: 'boolean', value: true, description: 'Enable NBFT support in the initrd generator') option('nbft', type: 'boolean', value: true, description: 'Enable NBFT support in the initrd generator')
option('clat', type: 'boolean', value: true, description: 'Build with CLAT support')
option('bpf-compiler', type : 'combo', choices : ['auto', 'clang', 'gcc'],
description : 'compiler used to build BPF programs')
# configuration plugins # configuration plugins
option('config_plugins_default', type: 'string', value: '', description: 'Default configuration option for main.plugins setting, used as fallback if the configuration option is unset') option('config_plugins_default', type: 'string', value: '', description: 'Default configuration option for main.plugins setting, used as fallback if the configuration option is unset')

View file

@ -30,6 +30,7 @@ id
it it
ja ja
ka ka
kk
kn kn
ko ko
ku ku

View file

@ -1,16 +1,18 @@
# List of source files containing translatable strings. # List of source files containing translatable strings.
# Please keep this file sorted alphabetically. # Please keep this file sorted alphabetically.
data/org.freedesktop.NetworkManager.policy.in.in data/org.freedesktop.NetworkManager.policy.in
src/core/NetworkManagerUtils.c src/core/NetworkManagerUtils.c
src/core/devices/adsl/nm-device-adsl.c src/core/devices/adsl/nm-device-adsl.c
src/core/devices/bluetooth/nm-bluez-manager.c src/core/devices/bluetooth/nm-bluez-manager.c
src/core/devices/bluetooth/nm-device-bt.c src/core/devices/bluetooth/nm-device-bt.c
src/core/devices/nm-device.c
src/core/devices/nm-device-6lowpan.c src/core/devices/nm-device-6lowpan.c
src/core/devices/nm-device-bond.c src/core/devices/nm-device-bond.c
src/core/devices/nm-device-bridge.c src/core/devices/nm-device-bridge.c
src/core/devices/nm-device-dummy.c src/core/devices/nm-device-dummy.c
src/core/devices/nm-device-ethernet-utils.c src/core/devices/nm-device-ethernet-utils.c
src/core/devices/nm-device-ethernet.c src/core/devices/nm-device-ethernet.c
src/core/devices/nm-device-geneve.c
src/core/devices/nm-device-infiniband.c src/core/devices/nm-device-infiniband.c
src/core/devices/nm-device-ip-tunnel.c src/core/devices/nm-device-ip-tunnel.c
src/core/devices/nm-device-loopback.c src/core/devices/nm-device-loopback.c
@ -46,6 +48,7 @@ src/libnm-client-impl/nm-device-bt.c
src/libnm-client-impl/nm-device-dummy.c src/libnm-client-impl/nm-device-dummy.c
src/libnm-client-impl/nm-device-ethernet.c src/libnm-client-impl/nm-device-ethernet.c
src/libnm-client-impl/nm-device-generic.c src/libnm-client-impl/nm-device-generic.c
src/libnm-client-impl/nm-device-geneve.c
src/libnm-client-impl/nm-device-hsr.c src/libnm-client-impl/nm-device-hsr.c
src/libnm-client-impl/nm-device-infiniband.c src/libnm-client-impl/nm-device-infiniband.c
src/libnm-client-impl/nm-device-ip-tunnel.c src/libnm-client-impl/nm-device-ip-tunnel.c
@ -90,6 +93,7 @@ src/libnm-core-impl/nm-setting-connection.c
src/libnm-core-impl/nm-setting-dcb.c src/libnm-core-impl/nm-setting-dcb.c
src/libnm-core-impl/nm-setting-ethtool.c src/libnm-core-impl/nm-setting-ethtool.c
src/libnm-core-impl/nm-setting-generic.c src/libnm-core-impl/nm-setting-generic.c
src/libnm-core-impl/nm-setting-geneve.c
src/libnm-core-impl/nm-setting-gsm.c src/libnm-core-impl/nm-setting-gsm.c
src/libnm-core-impl/nm-setting-hsr.c src/libnm-core-impl/nm-setting-hsr.c
src/libnm-core-impl/nm-setting-infiniband.c src/libnm-core-impl/nm-setting-infiniband.c

9918
po/bg.po

File diff suppressed because it is too large Load diff

16300
po/kk.po Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

5929
po/sl.po

File diff suppressed because it is too large Load diff

5388
po/tr.po

File diff suppressed because it is too large Load diff

View file

@ -1495,11 +1495,10 @@ nm_utils_ip_route_attribute_to_platform(int addr_family,
r4->scope_inv = nm_platform_route_scope_inv(scope); r4->scope_inv = nm_platform_route_scope_inv(scope);
} }
/* Note that for IPv4 routes in kernel, the onlink flag can be set for /* For IPv4 routes in kernel, the onlink flag is per-nexthop (rtnh_flags).
* each next hop separately (rtnh_flags). Not for NetworkManager. We can * Here we set the flag on r_rtm_flags which represents the first nexthop's
* only merge routes as ECMP routes (when setting a weight) if they all * flags. For ECMP routes, each nexthop carries its own onlink flag, so
* share the same onlink flag. See NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID. * routes with different onlink settings per-nexthop can be merged. */
* That simplifies the code. */
GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE); GET_ATTR(NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE);
r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u); r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u);

1203
src/core/bpf/clat.bpf.c Normal file

File diff suppressed because it is too large Load diff

30
src/core/bpf/clat.h Normal file
View file

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __NAT64_H__
#define __NAT64_H__
#include <linux/in6.h>
struct clat_config {
struct in6_addr local_v6;
struct in6_addr pref64;
struct in_addr local_v4;
unsigned pref64_len;
};
struct clat_stats {
/* egress: v4 to v6 */
__u64 egress_tcp;
__u64 egress_udp;
__u64 egress_icmp;
__u64 egress_other;
__u64 egress_dropped;
/* ingress: v6 to v4 */
__u64 ingress_tcp;
__u64 ingress_udp;
__u64 ingress_icmp;
__u64 ingress_other;
__u64 ingress_fragment;
__u64 ingress_dropped;
};
#endif

239
src/core/bpf/meson.build Normal file
View file

@ -0,0 +1,239 @@
# SPDX-License-Identifier: LGPL-2.1+
# Ripped from systemd: https://github.com/systemd/systemd/pull/20429
if not enable_clat
subdir_done()
endif
bpf_compiler = get_option('bpf-compiler')
clang_found = false
clang_supports_bpf = false
bpf_gcc_found = false
bpftool_strip = false
if bpf_compiler == 'clang' or bpf_compiler == 'auto'
# Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
# (like clang-10/llvm-strip-10)
if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
r = find_program('clang',
version : '>= 10.0.0')
clang_found = r.found()
if clang_found
if meson.version().version_compare('>= 0.55')
clang = r.full_path()
else
clang = r.path()
endif
endif
else
clang_found = true
clang = cc.cmd_array()
endif
if clang_found
# Check if 'clang -target bpf' is supported.
clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', check : false).returncode() == 0
endif
elif bpf_compiler == 'gcc' or bpf_compiler == 'auto'
bpf_gcc = find_program('bpf-gcc',
'bpf-none-gcc',
'bpf-unknown-none-gcc',
version : '>= 13.1.0')
bpf_gcc_found = bpf_gcc.found()
endif
if bpf_compiler == 'auto'
if clang_supports_bpf and bpf_gcc_found
# Both supported, prefer the one matching our compiler:
if cc.get_id() == 'gcc'
bpf_compiler = 'gcc'
else
# Default to clang if we don't know this compiler
bpf_compiler = 'clang'
endif
elif clang_supports_bpf
bpf_compiler = 'clang'
elif bpf_gcc_found
bpf_compiler = 'clang'
endif
endif
if clang_supports_bpf or bpf_gcc_found
# Debian installs this in /usr/sbin/ which is not in $PATH.
# We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
# We use 'bpftool gen object' subcommand for bpftool strip, it was added by d80b2fcbe0a023619e0fc73112f2a02c2662f6ab (v5.13).
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
required : bpf_compiler == 'gcc',
version : bpf_compiler == 'gcc' ? '>= 7.0.0' : '>= 5.13.0')
if bpftool.found()
bpftool_strip = true
elif bpf_compiler == 'clang'
# We require the 'bpftool gen skeleton' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
required : true,
version : '>= 5.6.0')
endif
# We use `llvm-strip` as a fallback if `bpftool gen object` strip support is not available.
if not bpftool_strip and bpftool.found() and clang_supports_bpf
if not meson.is_cross_build()
llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
check : true).stdout().strip()
else
llvm_strip_bin = 'llvm-strip'
endif
llvm_strip = find_program(llvm_strip_bin,
required : true,
version : '>= 10.0.0')
endif
else
error('clat support was enabled but couldn\'t find a suitable BPF compiler!')
endif
bpf_clang_flags = [
'-std=gnu17',
'-Wunused',
'-Wimplicit-fallthrough',
'-Wno-compare-distinct-pointer-types',
'-fno-stack-protector',
'-O2',
'-target',
'bpf',
'-g',
'-c',
]
bpf_gcc_flags = [
'-std=gnu17',
'-Wunused',
'-Wimplicit-fallthrough',
'-fno-stack-protector',
'-fno-ssa-phiopt',
'-O2',
'-mcpu=v3',
'-mco-re',
'-gbtf',
'-c',
]
clang_arch_flag = '-D__@0@__'.format(host_machine.cpu_family())
libbpf_include_dir = dependency('libbpf').get_variable(pkgconfig : 'includedir')
# Generate defines that are appropriate to tell the compiler what architecture
# we're compiling for. By default we just map meson's cpu_family to __<cpu_family>__.
# This dictionary contains the exceptions where this doesn't work.
#
# C.f. https://mesonbuild.com/Reference-tables.html#cpu-families
# and src/basic/missing_syscall_def.h.
cpu_arch_defines = {
'ppc' : ['-D__powerpc__', '-D__TARGET_ARCH_powerpc'],
'ppc64' : ['-D__powerpc64__', '-D__TARGET_ARCH_powerpc', '-D_CALL_ELF=2'],
'riscv32' : ['-D__riscv', '-D__riscv_xlen=32', '-D__TARGET_ARCH_riscv'],
'riscv64' : ['-D__riscv', '-D__riscv_xlen=64', '-D__TARGET_ARCH_riscv'],
'x86' : ['-D__i386__', '-D__TARGET_ARCH_x86'],
's390x' : ['-D__s390__', '-D__s390x__', '-D__TARGET_ARCH_s390'],
# For arm, assume hardware fp is available.
'arm' : ['-D__arm__', '-D__ARM_PCS_VFP', '-D__TARGET_ARCH_arm'],
'loongarch64' : ['-D__loongarch__', '-D__loongarch_grlen=64', '-D__TARGET_ARCH_loongarch']
}
bpf_arch_flags = cpu_arch_defines.get(host_machine.cpu_family(),
['-D__@0@__'.format(host_machine.cpu_family())])
if bpf_compiler == 'gcc'
bpf_arch_flags += ['-m' + host_machine.endian() + '-endian']
endif
bpf_o_unstripped_cmd = []
if bpf_compiler == 'clang'
bpf_o_unstripped_cmd += [
clang,
bpf_clang_flags,
bpf_arch_flags,
]
elif bpf_compiler == 'gcc'
bpf_o_unstripped_cmd += [
bpf_gcc,
bpf_gcc_flags,
bpf_arch_flags,
]
endif
bpf_o_unstripped_cmd += ['-I.']
if cc.get_id() == 'gcc' or meson.is_cross_build()
if cc.get_id() != 'gcc'
warning('Cross compiler is not gcc. Guessing the target triplet for bpf likely fails.')
endif
target_triplet_cmd = run_command(cc.cmd_array(), '-print-multiarch', check: false)
else
# clang does not support -print-multiarch (D133170) and its -dump-machine
# does not match multiarch. Query gcc instead.
target_triplet_cmd = run_command('gcc', '-print-multiarch', check: false)
endif
if target_triplet_cmd.returncode() == 0
target_triplet = target_triplet_cmd.stdout().strip()
bpf_o_unstripped_cmd += [
'-isystem',
'/usr/include/@0@'.format(target_triplet)
]
endif
bpf_o_unstripped_cmd += [
'-idirafter',
libbpf_include_dir,
'@INPUT@',
'-o',
'@OUTPUT@'
]
if bpftool_strip
bpf_o_cmd = [
bpftool,
'gen',
'object',
'@OUTPUT@',
'@INPUT@'
]
elif bpf_compiler == 'clang'
bpf_o_cmd = [
llvm_strip,
'-g',
'@INPUT@',
'-o',
'@OUTPUT@'
]
endif
skel_h_cmd = [
bpftool,
'g',
's',
'@INPUT@'
]
clat_bpf_o_unstripped = custom_target(
'clat.bpf.unstripped.o',
input : 'clat.bpf.c',
output : 'clat.bpf.unstripped.o',
command : bpf_o_unstripped_cmd)
clat_bpf_o = custom_target(
'clat.bpf.o',
input : clat_bpf_o_unstripped,
output : 'clat.bpf.o',
command : bpf_o_cmd)
clat_skel_h = custom_target(
'clat.skel.h',
input : clat_bpf_o,
output : 'clat.skel.h',
command : skel_h_cmd,
capture : true)

View file

@ -412,6 +412,7 @@ nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callb
_ADD_INTERNAL(nm_dummy_device_factory_get_type); _ADD_INTERNAL(nm_dummy_device_factory_get_type);
_ADD_INTERNAL(nm_ethernet_device_factory_get_type); _ADD_INTERNAL(nm_ethernet_device_factory_get_type);
_ADD_INTERNAL(nm_generic_device_factory_get_type); _ADD_INTERNAL(nm_generic_device_factory_get_type);
_ADD_INTERNAL(nm_geneve_device_factory_get_type);
_ADD_INTERNAL(nm_hsr_device_factory_get_type); _ADD_INTERNAL(nm_hsr_device_factory_get_type);
_ADD_INTERNAL(nm_infiniband_device_factory_get_type); _ADD_INTERNAL(nm_infiniband_device_factory_get_type);
_ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type); _ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type);

View file

@ -0,0 +1,487 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2026 Red Hat, Inc.
*/
#include "src/core/nm-default-daemon.h"
#include "nm-manager.h"
#include "nm-device-geneve.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-act-request.h"
#include "nm-device-private.h"
#include "nm-setting-geneve.h"
#include "libnm-platform/nm-platform.h"
#include "nm-device-factory.h"
#define _NMLOG_DEVICE_TYPE NMDeviceGeneve
#include "nm-device-logging.h"
NM_GOBJECT_PROPERTIES_DEFINE(NMDeviceGeneve,
PROP_ID,
PROP_REMOTE,
PROP_TOS,
PROP_TTL,
PROP_DF,
PROP_DST_PORT, );
typedef struct {
NMPlatformLnkGeneve props;
} NMDeviceGenevePrivate;
struct _NMDeviceGeneve {
NMDevice parent;
NMDeviceGenevePrivate _priv;
};
struct _NMDeviceGeneveClass {
NMDeviceClass parent;
};
G_DEFINE_TYPE(NMDeviceGeneve, nm_device_geneve, NM_TYPE_DEVICE)
#define NM_DEVICE_GENEVE_GET_PRIVATE(self) \
_NM_GET_PRIVATE(self, NMDeviceGeneve, NM_IS_DEVICE_GENEVE, NMDevice)
/*****************************************************************************/
static NMDeviceCapabilities
get_generic_capabilities(NMDevice *dev)
{
return NM_DEVICE_CAP_IS_SOFTWARE;
}
static void
update_properties(NMDevice *device)
{
NMDeviceGeneve *self;
NMDeviceGenevePrivate *priv;
const NMPlatformLink *plink;
const NMPlatformLnkGeneve *props;
int ifindex;
g_return_if_fail(NM_IS_DEVICE_GENEVE(device));
self = NM_DEVICE_GENEVE(device);
priv = NM_DEVICE_GENEVE_GET_PRIVATE(self);
ifindex = nm_device_get_ifindex(device);
g_return_if_fail(ifindex > 0);
props = nm_platform_link_get_lnk_geneve(nm_device_get_platform(device), ifindex, &plink);
if (!props) {
_LOGW(LOGD_PLATFORM, "could not get GENEVE properties");
return;
}
g_object_freeze_notify((GObject *) device);
#define CHECK_PROPERTY_CHANGED(field, prop) \
G_STMT_START \
{ \
if (priv->props.field != props->field) { \
priv->props.field = props->field; \
_notify(self, prop); \
} \
} \
G_STMT_END
#define CHECK_PROPERTY_CHANGED_IN6ADDR(field, prop) \
G_STMT_START \
{ \
if (memcmp(&priv->props.field, &props->field, sizeof(props->field)) != 0) { \
priv->props.field = props->field; \
_notify(self, prop); \
} \
} \
G_STMT_END
CHECK_PROPERTY_CHANGED(id, PROP_ID);
CHECK_PROPERTY_CHANGED(remote, PROP_REMOTE);
CHECK_PROPERTY_CHANGED_IN6ADDR(remote6, PROP_REMOTE);
CHECK_PROPERTY_CHANGED(tos, PROP_TOS);
CHECK_PROPERTY_CHANGED(ttl, PROP_TTL);
CHECK_PROPERTY_CHANGED(df, PROP_DF);
CHECK_PROPERTY_CHANGED(dst_port, PROP_DST_PORT);
g_object_thaw_notify((GObject *) device);
}
static void
link_changed(NMDevice *device, const NMPlatformLink *pllink)
{
NM_DEVICE_CLASS(nm_device_geneve_parent_class)->link_changed(device, pllink);
update_properties(device);
}
static void
unrealize_notify(NMDevice *device)
{
NMDeviceGeneve *self = NM_DEVICE_GENEVE(device);
NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(self);
guint i;
NM_DEVICE_CLASS(nm_device_geneve_parent_class)->unrealize_notify(device);
memset(&priv->props, 0, sizeof(NMPlatformLnkGeneve));
for (i = 1; i < _PROPERTY_ENUMS_LAST; i++)
g_object_notify_by_pspec(G_OBJECT(self), obj_properties[i]);
}
static gboolean
create_and_realize(NMDevice *device,
NMConnection *connection,
NMDevice *parent,
const NMPlatformLink **out_plink,
GError **error)
{
const char *iface = nm_device_get_iface(device);
NMPlatformLnkGeneve props = {};
NMSettingGeneve *s_geneve;
const char *str;
int r;
s_geneve = nm_connection_get_setting_geneve(connection);
g_return_val_if_fail(s_geneve, FALSE);
props.id = nm_setting_geneve_get_id(s_geneve);
str = nm_setting_geneve_get_remote(s_geneve);
if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.remote)
&& !nm_inet_parse_bin(AF_INET6, str, NULL, &props.remote6)) {
return nm_assert_unreachable_val(FALSE);
}
props.tos = nm_setting_geneve_get_tos(s_geneve);
props.ttl = nm_setting_geneve_get_ttl(s_geneve);
props.df = nm_setting_geneve_get_df(s_geneve);
props.dst_port = nm_setting_geneve_get_destination_port(s_geneve);
r = nm_platform_link_geneve_add(nm_device_get_platform(device), iface, &props, out_plink);
if (r < 0) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_CREATION_FAILED,
"Failed to create geneve interface '%s' for '%s': %s",
iface,
nm_connection_get_id(connection),
nm_strerror(r));
return FALSE;
}
return TRUE;
}
static gboolean
address_matches(const char *candidate, in_addr_t addr4, struct in6_addr *addr6)
{
NMIPAddr candidate_addr;
int addr_family;
if (!candidate)
return addr4 == 0u && IN6_IS_ADDR_UNSPECIFIED(addr6);
if (!nm_inet_parse_bin(AF_UNSPEC, candidate, &addr_family, &candidate_addr))
return FALSE;
if (!nm_ip_addr_equal(addr_family,
&candidate_addr,
NM_IS_IPv4(addr_family) ? (gpointer) &addr4 : addr6))
return FALSE;
if (NM_IS_IPv4(addr_family))
return IN6_IS_ADDR_UNSPECIFIED(addr6);
else
return addr4 == 0u;
}
static gboolean
check_connection_compatible(NMDevice *device,
NMConnection *connection,
gboolean check_properties,
GError **error)
{
NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(device);
NMSettingGeneve *s_geneve;
if (!NM_DEVICE_CLASS(nm_device_geneve_parent_class)
->check_connection_compatible(device, connection, check_properties, error))
return FALSE;
if (check_properties && nm_device_is_real(device)) {
s_geneve = nm_connection_get_setting_geneve(connection);
if (priv->props.id != nm_setting_geneve_get_id(s_geneve)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"geneve id mismatches");
return FALSE;
}
if (!address_matches(nm_setting_geneve_get_remote(s_geneve),
priv->props.remote,
&priv->props.remote6)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"geneve remote address mismatches");
return FALSE;
}
if (priv->props.dst_port != nm_setting_geneve_get_destination_port(s_geneve)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"geneve destination port mismatches");
return FALSE;
}
if (priv->props.tos != nm_setting_geneve_get_tos(s_geneve)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"geneve TOS mismatches");
return FALSE;
}
if (priv->props.ttl != nm_setting_geneve_get_ttl(s_geneve)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"geneve TTL mismatches");
return FALSE;
}
if (priv->props.df != nm_setting_geneve_get_df(s_geneve)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"geneve DF mismatches");
return FALSE;
}
}
return TRUE;
}
static gboolean
complete_connection(NMDevice *device,
NMConnection *connection,
const char *specific_object,
NMConnection *const *existing_connections,
GError **error)
{
NMSettingGeneve *s_geneve;
nm_utils_complete_generic(nm_device_get_platform(device),
connection,
NM_SETTING_GENEVE_SETTING_NAME,
existing_connections,
NULL,
_("Geneve connection"),
NULL,
NULL);
s_geneve = nm_connection_get_setting_geneve(connection);
if (!s_geneve) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_CONNECTION,
"A 'geneve' setting is required.");
return FALSE;
}
return TRUE;
}
static void
update_connection(NMDevice *device, NMConnection *connection)
{
NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(device);
NMSettingGeneve *s_geneve = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_GENEVE);
char sbuf[NM_INET_ADDRSTRLEN];
if (priv->props.id != nm_setting_geneve_get_id(s_geneve))
g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_ID, priv->props.id, NULL);
/* Handle remote (IPv4 or IPv6) */
if (priv->props.remote) {
g_object_set(s_geneve,
NM_SETTING_GENEVE_REMOTE,
nm_inet4_ntop(priv->props.remote, sbuf),
NULL);
} else if (memcmp(&priv->props.remote6, &in6addr_any, sizeof(in6addr_any))) {
g_object_set(s_geneve,
NM_SETTING_GENEVE_REMOTE,
nm_inet6_ntop(&priv->props.remote6, sbuf),
NULL);
}
if (priv->props.dst_port != nm_setting_geneve_get_destination_port(s_geneve))
g_object_set(G_OBJECT(s_geneve),
NM_SETTING_GENEVE_DESTINATION_PORT,
priv->props.dst_port,
NULL);
if (priv->props.tos != nm_setting_geneve_get_tos(s_geneve))
g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_TOS, priv->props.tos, NULL);
if (priv->props.ttl != nm_setting_geneve_get_ttl(s_geneve))
g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_TTL, priv->props.ttl, NULL);
if (priv->props.df != nm_setting_geneve_get_df(s_geneve))
g_object_set(G_OBJECT(s_geneve), NM_SETTING_GENEVE_DF, priv->props.df, NULL);
}
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(object);
switch (prop_id) {
case PROP_ID:
g_value_set_uint(value, priv->props.id);
break;
case PROP_REMOTE:
if (priv->props.remote)
g_value_take_string(value, nm_inet4_ntop_dup(priv->props.remote));
else if (!IN6_IS_ADDR_UNSPECIFIED(&priv->props.remote6))
g_value_take_string(value, nm_inet6_ntop_dup(&priv->props.remote6));
break;
case PROP_TOS:
g_value_set_uchar(value, priv->props.tos);
break;
case PROP_TTL:
g_value_set_uchar(value, priv->props.ttl);
break;
case PROP_DF:
g_value_set_uint(value, priv->props.df);
break;
case PROP_DST_PORT:
g_value_set_uint(value, priv->props.dst_port);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_device_geneve_init(NMDeviceGeneve *self)
{}
static const NMDBusInterfaceInfoExtended interface_info_device_geneve = {
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
NM_DBUS_INTERFACE_DEVICE_GENEVE,
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Id", "u", NM_DEVICE_GENEVE_ID),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Remote", "s", NM_DEVICE_GENEVE_REMOTE),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tos", "y", NM_DEVICE_GENEVE_TOS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ttl", "y", NM_DEVICE_GENEVE_TTL),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Df", "u", NM_DEVICE_GENEVE_DF),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("DstPort",
"q",
NM_DEVICE_GENEVE_DST_PORT), ), ),
};
static void
nm_device_geneve_class_init(NMDeviceGeneveClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
object_class->get_property = get_property;
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_geneve);
device_class->connection_type_supported = NM_SETTING_GENEVE_SETTING_NAME;
device_class->connection_type_check_compatible = NM_SETTING_GENEVE_SETTING_NAME;
device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_GENEVE);
device_class->link_changed = link_changed;
device_class->unrealize_notify = unrealize_notify;
device_class->create_and_realize = create_and_realize;
device_class->check_connection_compatible = check_connection_compatible;
device_class->complete_connection = complete_connection;
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->update_connection = update_connection;
obj_properties[PROP_ID] = g_param_spec_uint(NM_DEVICE_GENEVE_ID,
"",
"",
0,
G_MAXUINT32,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_REMOTE] = g_param_spec_string(NM_DEVICE_GENEVE_REMOTE,
"",
"",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_GENEVE_TOS,
"",
"",
0,
255,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_TTL] = g_param_spec_uchar(NM_DEVICE_GENEVE_TTL,
"",
"",
0,
255,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_DF] = g_param_spec_uint(NM_DEVICE_GENEVE_DF,
"",
"",
0,
2,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_DST_PORT] = g_param_spec_uint(NM_DEVICE_GENEVE_DST_PORT,
"",
"",
0,
65535,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}
/*****************************************************************************/
#define NM_TYPE_GENEVE_DEVICE_FACTORY (nm_geneve_device_factory_get_type())
#define NM_GENEVE_DEVICE_FACTORY(obj) \
(_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_GENEVE_DEVICE_FACTORY, NMGeneveDeviceFactory))
static NMDevice *
create_device(NMDeviceFactory *factory,
const char *iface,
const NMPlatformLink *plink,
NMConnection *connection,
gboolean *out_ignore)
{
return g_object_new(NM_TYPE_DEVICE_GENEVE,
NM_DEVICE_IFACE,
iface,
NM_DEVICE_TYPE_DESC,
"Geneve",
NM_DEVICE_DEVICE_TYPE,
NM_DEVICE_TYPE_GENEVE,
NM_DEVICE_LINK_TYPE,
NM_LINK_TYPE_GENEVE,
NULL);
}
NM_DEVICE_FACTORY_DEFINE_INTERNAL(
GENEVE,
Geneve,
geneve,
NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_GENEVE)
NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_GENEVE_SETTING_NAME),
factory_class->create_device = create_device;);

View file

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2026 Red Hat, Inc.
*/
#ifndef __NETWORKMANAGER_DEVICE_GENEVE_H__
#define __NETWORKMANAGER_DEVICE_GENEVE_H__
#include "nm-device.h"
#define NM_TYPE_DEVICE_GENEVE (nm_device_geneve_get_type())
#define NM_DEVICE_GENEVE(obj) \
(_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneve))
#define NM_DEVICE_GENEVE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneveClass))
#define NM_IS_DEVICE_GENEVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_GENEVE))
#define NM_IS_DEVICE_GENEVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_GENEVE))
#define NM_DEVICE_GENEVE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_GENEVE, NMDeviceGeneveClass))
#define NM_DEVICE_GENEVE_ID "id"
#define NM_DEVICE_GENEVE_REMOTE "remote"
#define NM_DEVICE_GENEVE_TOS "tos"
#define NM_DEVICE_GENEVE_TTL "ttl"
#define NM_DEVICE_GENEVE_DF "df"
#define NM_DEVICE_GENEVE_DST_PORT "dst-port"
typedef struct _NMDeviceGeneve NMDeviceGeneve;
typedef struct _NMDeviceGeneveClass NMDeviceGeneveClass;
GType nm_device_geneve_get_type(void);
#endif /* __NETWORKMANAGER_DEVICE_GENEVE_H__ */

View file

@ -176,14 +176,14 @@ create_and_realize(NMDevice *device,
if (str) { if (str) {
if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.local) if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.local)
&& !nm_inet_parse_bin(AF_INET6, str, NULL, &props.local6)) && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.local6))
return FALSE; return nm_assert_unreachable_val(FALSE);
} }
str = nm_setting_vxlan_get_remote(s_vxlan); str = nm_setting_vxlan_get_remote(s_vxlan);
if (str) { if (str) {
if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.group) if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.group)
&& !nm_inet_parse_bin(AF_INET6, str, NULL, &props.group6)) && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.group6))
return FALSE; return nm_assert_unreachable_val(FALSE);
} }
props.tos = nm_setting_vxlan_get_tos(s_vxlan); props.tos = nm_setting_vxlan_get_tos(s_vxlan);

File diff suppressed because it is too large Load diff

View file

@ -791,6 +791,7 @@ void nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_fr
void nm_device_update_dynamic_ip_setup(NMDevice *self, const char *reason); void nm_device_update_dynamic_ip_setup(NMDevice *self, const char *reason);
guint nm_device_get_supplicant_timeout(NMDevice *self); guint nm_device_get_supplicant_timeout(NMDevice *self);
gboolean nm_device_auth_retries_has_next(NMDevice *self);
gboolean nm_device_auth_retries_try_next(NMDevice *self); gboolean nm_device_auth_retries_try_next(NMDevice *self);
gboolean nm_device_hw_addr_get_cloned(NMDevice *self, gboolean nm_device_hw_addr_get_cloned(NMDevice *self,

View file

@ -2270,6 +2270,37 @@ add_new:
return NM_ACT_STAGE_RETURN_SUCCESS; return NM_ACT_STAGE_RETURN_SUCCESS;
} }
static void
set_powersave(NMDevice *device)
{
NMDeviceIwd *self = NM_DEVICE_IWD(device);
NMSettingWireless *s_wireless;
NMSettingWirelessPowersave val;
s_wireless = nm_device_get_applied_setting(device, NM_TYPE_SETTING_WIRELESS);
g_return_if_fail(s_wireless);
val = nm_setting_wireless_get_powersave(s_wireless);
if (val == NM_SETTING_WIRELESS_POWERSAVE_DEFAULT) {
val = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
"wifi.powersave",
device,
NM_SETTING_WIRELESS_POWERSAVE_IGNORE,
NM_SETTING_WIRELESS_POWERSAVE_ENABLE,
NM_SETTING_WIRELESS_POWERSAVE_IGNORE);
}
_LOGT(LOGD_WIFI, "powersave is set to %u", (unsigned) val);
if (val == NM_SETTING_WIRELESS_POWERSAVE_IGNORE)
return;
nm_platform_wifi_set_powersave(nm_device_get_platform(device),
nm_device_get_ifindex(device),
val == NM_SETTING_WIRELESS_POWERSAVE_ENABLE);
}
static NMActStageReturn static NMActStageReturn
act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
{ {
@ -2297,6 +2328,8 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
goto out_fail; goto out_fail;
} }
set_powersave(device);
/* With priv->iwd_autoconnect we have to let IWD handle retries for /* With priv->iwd_autoconnect we have to let IWD handle retries for
* infrastructure networks. IWD will not necessarily retry the same * infrastructure networks. IWD will not necessarily retry the same
* network after a failure but it will likely go into an autoconnect * network after a failure but it will likely go into an autoconnect

View file

@ -194,6 +194,9 @@ static void supplicant_iface_notify_p2p_available(NMSupplicantInterface *iface,
static void supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, static void supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface,
NMDeviceWifi *self); NMDeviceWifi *self);
static void supplicant_iface_notify_wpa_sae_mismatch_cb(NMSupplicantInterface *iface,
NMDeviceWifi *self);
static void periodic_update(NMDeviceWifi *self); static void periodic_update(NMDeviceWifi *self);
static void ap_add_remove(NMDeviceWifi *self, static void ap_add_remove(NMDeviceWifi *self,
@ -631,6 +634,10 @@ supplicant_interface_acquire_cb(NMSupplicantManager *supplicant_manager,
NM_SUPPLICANT_INTERFACE_PSK_MISMATCH, NM_SUPPLICANT_INTERFACE_PSK_MISMATCH,
G_CALLBACK(supplicant_iface_notify_wpa_psk_mismatch_cb), G_CALLBACK(supplicant_iface_notify_wpa_psk_mismatch_cb),
self); self);
g_signal_connect(priv->sup_iface,
NM_SUPPLICANT_INTERFACE_SAE_MISMATCH,
G_CALLBACK(supplicant_iface_notify_wpa_sae_mismatch_cb),
self);
_scan_notify_is_scanning(self); _scan_notify_is_scanning(self);
@ -2244,6 +2251,26 @@ wps_timeout_cb(gpointer user_data)
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
static gboolean
wifi_connection_is_new(NMDeviceWifi *self)
{
NMDevice *device = NM_DEVICE(self);
NMActRequest *req;
NMSettingsConnection *connection;
guint64 timestamp = 0;
req = nm_device_get_act_request(device);
g_return_val_if_fail(NM_IS_ACT_REQUEST(req), TRUE);
connection = nm_act_request_get_settings_connection(req);
g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(connection), TRUE);
if (nm_settings_connection_get_timestamp(connection, &timestamp) && timestamp != 0)
return FALSE;
return TRUE;
}
static void static void
wifi_secrets_get_secrets(NMDeviceWifi *self, wifi_secrets_get_secrets(NMDeviceWifi *self,
const char *setting_name, const char *setting_name,
@ -2398,10 +2425,11 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self,
NMSupplicantInterfaceState old_state, NMSupplicantInterfaceState old_state,
int disconnect_reason) int disconnect_reason)
{ {
NMDevice *device = NM_DEVICE(self); NMDevice *device = NM_DEVICE(self);
NMActRequest *req; NMActRequest *req;
const char *setting_name = NULL; const char *setting_name = NULL;
gboolean handled = FALSE; NMSecretAgentGetSecretsFlags secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
| NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
g_return_val_if_fail(new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); g_return_val_if_fail(new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE);
@ -2411,8 +2439,7 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self,
req = nm_device_get_act_request(NM_DEVICE(self)); req = nm_device_get_act_request(NM_DEVICE(self));
g_return_val_if_fail(req != NULL, FALSE); g_return_val_if_fail(req != NULL, FALSE);
if (need_new_8021x_secrets(self, old_state, &setting_name) if (need_new_8021x_secrets(self, old_state, &setting_name)) {
|| need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) {
nm_act_request_clear_secrets(req); nm_act_request_clear_secrets(req);
_LOGI(LOGD_DEVICE | LOGD_WIFI, _LOGI(LOGD_DEVICE | LOGD_WIFI,
@ -2422,14 +2449,54 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self,
nm_device_state_changed(device, nm_device_state_changed(device,
NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_NEED_AUTH,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
wifi_secrets_get_secrets(self, wifi_secrets_get_secrets(self, setting_name, secret_flags);
setting_name, return TRUE;
NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
| NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
handled = TRUE;
} }
return handled; if (need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) {
nm_act_request_clear_secrets(req);
cleanup_association_attempt(self, TRUE);
if (wifi_connection_is_new(self)) {
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) new connection disconnected during association, asking for "
"new key");
nm_device_state_changed(device,
NM_DEVICE_STATE_NEED_AUTH,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
wifi_secrets_get_secrets(self, setting_name, secret_flags);
return TRUE;
}
if (!nm_device_auth_retries_try_next(device)) {
nm_device_state_changed(device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_NO_SECRETS);
return TRUE;
}
if (nm_device_auth_retries_has_next(device)) {
secret_flags &= ~NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
_LOGI(
LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) disconnected during association, reauthenticating connection");
} else {
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) disconnected during association, asking for new key");
}
nm_device_state_changed(device,
NM_DEVICE_STATE_NEED_AUTH,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
wifi_secrets_get_secrets(self, setting_name, secret_flags);
return TRUE;
}
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) disconnected during association, retrying connection");
return FALSE;
} }
static gboolean static gboolean
@ -2861,6 +2928,12 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi
if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG) if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG)
return; return;
if (!wifi_connection_is_new(self) && nm_device_auth_retries_has_next(device)) {
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) psk mismatch reported by supplicant, retrying connection");
return;
}
_LOGI(LOGD_DEVICE | LOGD_WIFI, _LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) psk mismatch reported by supplicant, asking for new key"); "Activation: (wifi) psk mismatch reported by supplicant, asking for new key");
@ -2879,6 +2952,34 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi
| NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
} }
static void
supplicant_iface_notify_wpa_sae_mismatch_cb(NMSupplicantInterface *iface, NMDeviceWifi *self)
{
NMDevice *device = NM_DEVICE(self);
NMActRequest *req;
const char *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG)
return;
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) SAE password mismatch reported by supplicant, asking for new key");
req = nm_device_get_act_request(NM_DEVICE(self));
g_return_if_fail(req != NULL);
nm_act_request_clear_secrets(req);
cleanup_association_attempt(self, TRUE);
nm_device_state_changed(device,
NM_DEVICE_STATE_NEED_AUTH,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
wifi_secrets_get_secrets(self,
setting_name,
NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
| NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
}
/* /*
* supplicant_connection_timeout_cb * supplicant_connection_timeout_cb
* *
@ -3222,8 +3323,19 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
static void static void
ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap) ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap)
{ {
guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0}; guint32 freqs_a[] = {5180, /* only U-NII-1 channels: non-DFS and available everywhere */
guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0}; 5200,
5220,
5240,
0};
guint32 freqs_bg[] = {2412, 2437, 2462, 2472, 0};
guint32 freqs_6ghz[] = {5975, /* only U-NII-5 PSC channels, for better compatibility */
6055,
6135,
6215,
6295,
6375,
0};
guint32 *rnd_freqs; guint32 *rnd_freqs;
guint rnd_freqs_len; guint rnd_freqs_len;
NMDevice *device = NM_DEVICE(self); NMDevice *device = NM_DEVICE(self);
@ -3234,7 +3346,7 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP
guint l; guint l;
nm_assert(ap); nm_assert(ap);
nm_assert(NM_IN_STRSET(band, NULL, "a", "bg")); nm_assert(NM_IN_STRSET(band, NULL, "a", "bg", "6GHz"));
if (nm_wifi_ap_get_freq(ap)) if (nm_wifi_ap_get_freq(ap))
return; return;
@ -3268,11 +3380,14 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP
} }
if (nm_streq0(band, "a")) { if (nm_streq0(band, "a")) {
rnd_freqs = a_freqs; rnd_freqs = freqs_a;
rnd_freqs_len = G_N_ELEMENTS(a_freqs) - 1; rnd_freqs_len = G_N_ELEMENTS(freqs_a) - 1;
} else if (nm_streq0(band, "6GHz")) {
rnd_freqs = freqs_6ghz;
rnd_freqs_len = G_N_ELEMENTS(freqs_6ghz) - 1;
} else { } else {
rnd_freqs = bg_freqs; rnd_freqs = freqs_bg;
rnd_freqs_len = G_N_ELEMENTS(bg_freqs) - 1; rnd_freqs_len = G_N_ELEMENTS(freqs_bg) - 1;
} }
/* shuffle the frequencies (inplace). The idea is to choose /* shuffle the frequencies (inplace). The idea is to choose

View file

@ -574,16 +574,6 @@ nm_wifi_ap_to_string(const NMWifiAP *self, char *str_buf, gulong buf_len, gint64
return str_buf; return str_buf;
} }
static guint
freq_to_band(guint32 freq)
{
if (freq >= 4915 && freq <= 5825)
return 5;
else if (freq >= 2412 && freq <= 2484)
return 2;
return 0;
}
gboolean gboolean
nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection) nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection)
{ {
@ -631,12 +621,12 @@ nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection)
band = nm_setting_wireless_get_band(s_wireless); band = nm_setting_wireless_get_band(s_wireless);
if (band) { if (band) {
guint ap_band = freq_to_band(priv->freq); const char *ap_band = nm_wifi_freq_to_band_prop(priv->freq);
if (!strcmp(band, "a") && ap_band != 5) if (!nm_streq(band, ap_band))
return FALSE;
else if (!strcmp(band, "bg") && ap_band != 2)
return FALSE; return FALSE;
return TRUE;
} }
channel = nm_setting_wireless_get_channel(s_wireless); channel = nm_setting_wireless_get_channel(s_wireless);

View file

@ -639,7 +639,7 @@ nm_wifi_utils_complete_connection(GBytes *ap_ssid,
chan_valid = FALSE; chan_valid = FALSE;
} }
band = nm_utils_wifi_freq_to_band(ap_freq); band = nm_wifi_freq_to_band_prop(ap_freq);
if (band) { if (band) {
g_object_set(s_wifi, NM_SETTING_WIRELESS_BAND, band, NULL); g_object_set(s_wifi, NM_SETTING_WIRELESS_BAND, band, NULL);
} else { } else {
@ -1929,3 +1929,19 @@ nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b)
return a->source == b->source && a->sink == b->sink && a->port == b->port return a->source == b->source && a->sink == b->sink && a->port == b->port
&& a->has_audio == b->has_audio && a->has_uibc == b->has_uibc && a->has_cp == b->has_cp; && a->has_audio == b->has_audio && a->has_uibc == b->has_uibc && a->has_cp == b->has_cp;
} }
const char *
nm_wifi_freq_to_band_prop(guint32 freq)
{
switch (nm_utils_wifi_freq_to_band(freq)) {
case NM_WIFI_BAND_2_4_GHZ:
return "bg";
case NM_WIFI_BAND_5_GHZ:
return "a";
case NM_WIFI_BAND_6_GHZ:
return "6GHz";
default:
case NM_WIFI_BAND_UNKNOWN:
return NULL;
}
}

View file

@ -56,4 +56,6 @@ bool nm_wifi_utils_parse_wfd_ies(GBytes *ies, NMIwdWfdInfo *out_wfd);
GBytes *nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd); GBytes *nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd);
bool nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b); bool nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b);
const char *nm_wifi_freq_to_band_prop(guint32 freq);
#endif /* __NM_WIFI_UTILS_H__ */ #endif /* __NM_WIFI_UTILS_H__ */

View file

@ -258,16 +258,7 @@ modm_handle_name_owner_changed(MMManager *modem_manager, GParamSpec *pspec, NMMo
/* Available! */ /* Available! */
g_free(name_owner); g_free(name_owner);
/* Hack alert: GDBusObjectManagerClient won't signal neither 'object-added' modm_manager_available(self);
* nor 'object-removed' if it was created while there was no ModemManager in
* the bus. This hack avoids this issue until we get a GIO with the fix
* included... */
modm_clear_manager(self);
modm_ensure_manager(self);
/* Whenever GDBusObjectManagerClient is fixed, we can just do the following:
* modm_manager_available (self);
*/
} }
static void static void

View file

@ -1460,7 +1460,9 @@ nm_dhcp_client_schedule_ipv6_only_restart(NMDhcpClient *self, guint timeout)
nm_assert(!priv->is_stopped); nm_assert(!priv->is_stopped);
timeout = NM_MAX(priv->v4.ipv6_only_min_wait, timeout); timeout = NM_MAX(priv->v4.ipv6_only_min_wait, timeout);
_LOGI("received option \"ipv6-only-preferred\": stopping DHCPv4 for %u seconds", timeout); _LOGI("received option \"ipv6-only-preferred\": stopping DHCPv4 for %u seconds. Set "
"ipv4.dhcp-ipv6-only-preferred=no to force the use of IPv4 on this IPv6-mostly network",
timeout);
nm_dhcp_client_stop(self, FALSE); nm_dhcp_client_stop(self, FALSE);
nm_clear_g_source_inst(&priv->no_lease_timeout_source); nm_clear_g_source_inst(&priv->no_lease_timeout_source);

View file

@ -418,7 +418,6 @@ lease_parse_routes(NDhcp4ClientLease *lease,
in_addr_t gateway; in_addr_t gateway;
uint8_t plen; uint8_t plen;
guint32 m; guint32 m;
gboolean has_router_from_classless = FALSE;
gboolean has_classless = FALSE; gboolean has_classless = FALSE;
guint32 default_route_metric_offset = 0; guint32 default_route_metric_offset = 0;
const guint8 *l_data; const guint8 *l_data;
@ -434,7 +433,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
* We will however also parse one of the options into the "l3cd" for configuring routing. * We will however also parse one of the options into the "l3cd" for configuring routing.
* Thereby we prefer 121 over 249 over 33. * Thereby we prefer 121 over 249 over 33.
* *
* Preferring 121 over 33 is defined by RFC 3443. * Preferring 121 over 33 is defined by RFC 3442.
* Preferring 121 over 249 over 33 is made up as it makes sense (the MS docs are not very clear). * Preferring 121 over 249 over 33 is made up as it makes sense (the MS docs are not very clear).
*/ */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
@ -460,8 +459,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
if (plen == 0) { if (plen == 0) {
/* if there are multiple default routes, we add them with differing /* if there are multiple default routes, we add them with differing
* metrics. */ * metrics. */
m = default_route_metric_offset++; m = default_route_metric_offset++;
has_router_from_classless = TRUE;
} else } else
m = 0; m = 0;
@ -495,7 +493,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str); nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str);
if (has_classless) { if (has_classless) {
/* RFC 3443: if the DHCP server returns both a Classless Static Routes /* RFC 3442: if the DHCP server returns both a Classless Static Routes
* option and a Static Routes option, the DHCP client MUST ignore the * option and a Static Routes option, the DHCP client MUST ignore the
* Static Routes option. */ * Static Routes option. */
continue; continue;
@ -539,13 +537,10 @@ lease_parse_routes(NDhcp4ClientLease *lease,
continue; continue;
} }
if (has_router_from_classless) { if (has_classless) {
/* If the DHCP server returns both a Classless Static Routes option and a /* RFC 3442: if the DHCP server returns both a Classless Static Routes
* Router option, the DHCP client MUST ignore the Router option [RFC 3442]. * option and a Router option, the DHCP client MUST ignore the Router
* * option. */
* Be more lenient and ignore the Router option only if Classless Static
* Routes contain a default gateway (as other DHCP backends do).
*/
continue; continue;
} }

View file

@ -32,11 +32,11 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface,
in_addr_t address, in_addr_t address,
guint32 *out_gwaddr) guint32 *out_gwaddr)
{ {
gs_free const char **routes = NULL; gs_free char **routes = NULL;
const char **r; char **r;
gboolean have_routes = FALSE; gboolean have_routes = FALSE;
routes = nm_strsplit_set(str, " "); routes = (char **) nm_strsplit_set(str, " ");
if (!routes) if (!routes)
return FALSE; return FALSE;

View file

@ -1510,8 +1510,8 @@ _domain_track_is_shadowed(GHashTable *ht,
const char **out_parent, const char **out_parent,
int *out_parent_priority) int *out_parent_priority)
{ {
char *parent; const char *parent;
int parent_priority; int parent_priority;
if (!ht) if (!ht)
return FALSE; return FALSE;

View file

@ -298,12 +298,6 @@ main(int argc, char *argv[])
_nm_utils_is_manager_process = TRUE; _nm_utils_is_manager_process = TRUE;
/* Known to cause a possible deadlock upon GDBus initialization:
* https://bugzilla.gnome.org/show_bug.cgi?id=674885 */
g_type_ensure(G_TYPE_SOCKET);
g_type_ensure(G_TYPE_DBUS_CONNECTION);
g_type_ensure(NM_TYPE_DBUS_MANAGER);
/* we determine a first-start (contrary to a restart during the same boot) /* we determine a first-start (contrary to a restart during the same boot)
* based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */ * based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */
config_cli = nm_config_cmd_line_options_new( config_cli = nm_config_cmd_line_options_new(
@ -328,6 +322,12 @@ main(int argc, char *argv[])
exit(result); exit(result);
} }
/* Known to cause a possible deadlock upon GDBus initialization:
* https://bugzilla.gnome.org/show_bug.cgi?id=674885 */
g_type_ensure(G_TYPE_SOCKET);
g_type_ensure(G_TYPE_DBUS_CONNECTION);
g_type_ensure(NM_TYPE_DBUS_MANAGER);
nm_main_utils_ensure_not_running_pidfile(global_opt.pidfile); nm_main_utils_ensure_not_running_pidfile(global_opt.pidfile);
nm_main_utils_ensure_statedir(); nm_main_utils_ensure_statedir();
@ -461,14 +461,8 @@ main(int argc, char *argv[])
/* the first access to State causes the file to be read (and possibly print a warning) */ /* the first access to State causes the file to be read (and possibly print a warning) */
nm_config_state_get(config); nm_config_state_get(config);
nm_log_dbg(LOGD_CORE, nm_log_dbg(LOGD_CORE, "WEXT support is %s", HAVE_WEXT ? "enabled" : "disabled");
"WEXT support is %s", nm_log_dbg(LOGD_CORE, "CLAT support is %s", HAVE_CLAT ? "enabled" : "disabled");
#if HAVE_WEXT
"enabled"
#else
"disabled"
#endif
);
if (!_dbus_manager_init(config)) if (!_dbus_manager_init(config))
goto done_no_manager; goto done_no_manager;

View file

@ -32,6 +32,15 @@ install_data(
core_plugins = [] core_plugins = []
subdir('bpf')
base_sources_addon = []
base_deps_addon = []
if enable_clat
base_sources_addon += [clat_skel_h]
base_deps_addon += [libbpf]
endif
libNetworkManagerBase = static_library( libNetworkManagerBase = static_library(
'NetworkManagerBase', 'NetworkManagerBase',
sources: files( sources: files(
@ -55,13 +64,13 @@ libNetworkManagerBase = static_library(
'nm-l3cfg.c', 'nm-l3cfg.c',
'nm-bond-manager.c', 'nm-bond-manager.c',
'nm-ip-config.c', 'nm-ip-config.c',
), ) + base_sources_addon,
dependencies: [ dependencies: [
core_default_dep, core_default_dep,
libnm_core_public_dep, libnm_core_public_dep,
libsystemd_dep, libsystemd_dep,
libudev_dep, libudev_dep,
], ] + base_deps_addon,
) )
nm_deps = [ nm_deps = [
@ -102,6 +111,7 @@ libNetworkManager = static_library(
'devices/nm-device-ethernet-utils.c', 'devices/nm-device-ethernet-utils.c',
'devices/nm-device-factory.c', 'devices/nm-device-factory.c',
'devices/nm-device-generic.c', 'devices/nm-device-generic.c',
'devices/nm-device-geneve.c',
'devices/nm-device-hsr.c', 'devices/nm-device-hsr.c',
'devices/nm-device-infiniband.c', 'devices/nm-device-infiniband.c',
'devices/nm-device-ip-tunnel.c', 'devices/nm-device-ip-tunnel.c',

View file

@ -401,6 +401,30 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
} }
} }
#if HAVE_CLAT
/* PREF64 */
ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_PREF64) {
NMNDiscPref64 pref64;
pref64 = (NMNDiscPref64) {
.prefix = *ndp_msg_opt_pref64_prefix(msg, offset),
.plen = ndp_msg_opt_pref64_prefix_length(msg, offset),
.gateway = gateway.address,
.gateway_preference = gateway.preference,
.expiry_msec =
_nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_pref64_lifetime(msg, offset)),
.gateway_expiry_msec = gateway.expiry_msec,
};
/* libndp should only return lengths defined in RFC 8781 */
nm_assert(NM_IN_SET(pref64.plen, 96, 64, 56, 48, 40, 32));
if (nm_ndisc_add_pref64(ndisc, &pref64, now_msec)) {
changed |= NM_NDISC_CONFIG_PREF64;
}
}
#endif
nm_ndisc_ra_received(ndisc, now_msec, changed); nm_ndisc_ra_received(ndisc, now_msec, changed);
return 0; return 0;
} }

View file

@ -14,6 +14,7 @@ struct _NMNDiscDataInternal {
NMNDiscData public; NMNDiscData public;
GArray *gateways; GArray *gateways;
GArray *addresses; GArray *addresses;
GArray *pref64;
GArray *routes; GArray *routes;
GArray *dns_servers; GArray *dns_servers;
GArray *dns_domains; GArray *dns_domains;
@ -28,6 +29,7 @@ gboolean nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gi
gboolean gboolean
nm_ndisc_complete_and_add_address(NMNDisc *ndisc, const NMNDiscAddress *new_item, gint64 now_msec); nm_ndisc_complete_and_add_address(NMNDisc *ndisc, const NMNDiscAddress *new_item, gint64 now_msec);
gboolean nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec); gboolean nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec);
gboolean nm_ndisc_add_pref64(NMNDisc *ndisc, const NMNDiscPref64 *new_item, gint64 now_msec);
gboolean nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec); gboolean nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec);
gboolean nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new_item, gint64 now_msec); gboolean nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new_item, gint64 now_msec);

View file

@ -34,6 +34,7 @@
#define _SIZE_MAX_ROUTES 1000u #define _SIZE_MAX_ROUTES 1000u
#define _SIZE_MAX_DNS_SERVERS 64u #define _SIZE_MAX_DNS_SERVERS 64u
#define _SIZE_MAX_DNS_DOMAINS 64u #define _SIZE_MAX_DNS_DOMAINS 64u
#define _SIZE_MAX_PREF64 8u
/*****************************************************************************/ /*****************************************************************************/
@ -109,7 +110,8 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
int ifindex, int ifindex,
const NMNDiscData *rdata, const NMNDiscData *rdata,
NMSettingIP6ConfigPrivacy ip6_privacy, NMSettingIP6ConfigPrivacy ip6_privacy,
NMUtilsIPv6IfaceId *token) NMUtilsIPv6IfaceId *token,
const char *network_id)
{ {
nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
guint32 ifa_flags; guint32 ifa_flags;
@ -211,13 +213,21 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
for (i = 0; i < rdata->dns_domains_n; i++) for (i = 0; i < rdata->dns_domains_n; i++)
nm_l3_config_data_add_search(l3cd, AF_INET6, rdata->dns_domains[i].domain); nm_l3_config_data_add_search(l3cd, AF_INET6, rdata->dns_domains[i].domain);
if (rdata->pref64_n > 0) {
nm_l3_config_data_set_pref64(l3cd, rdata->pref64[0].prefix, rdata->pref64[0].plen);
} else {
nm_l3_config_data_set_pref64_valid(l3cd, FALSE);
}
nm_l3_config_data_set_ndisc_hop_limit(l3cd, rdata->hop_limit); nm_l3_config_data_set_ndisc_hop_limit(l3cd, rdata->hop_limit);
nm_l3_config_data_set_ndisc_reachable_time_msec(l3cd, rdata->reachable_time_ms); nm_l3_config_data_set_ndisc_reachable_time_msec(l3cd, rdata->reachable_time_ms);
nm_l3_config_data_set_ndisc_retrans_timer_msec(l3cd, rdata->retrans_timer_ms); nm_l3_config_data_set_ndisc_retrans_timer_msec(l3cd, rdata->retrans_timer_ms);
nm_l3_config_data_set_ip6_mtu(l3cd, rdata->mtu); nm_l3_config_data_set_ip6_mtu_ra(l3cd, rdata->mtu);
if (token) if (token)
nm_l3_config_data_set_ip6_token(l3cd, *token); nm_l3_config_data_set_ip6_token(l3cd, *token);
if (network_id)
nm_l3_config_data_set_network_id(l3cd, network_id);
return g_steal_pointer(&l3cd); return g_steal_pointer(&l3cd);
} }
@ -416,6 +426,7 @@ _data_complete(NMNDiscDataInternal *data)
_SET(data, gateways); _SET(data, gateways);
_SET(data, addresses); _SET(data, addresses);
_SET(data, routes); _SET(data, routes);
_SET(data, pref64);
_SET(data, dns_servers); _SET(data, dns_servers);
_SET(data, dns_domains); _SET(data, dns_domains);
#undef _SET #undef _SET
@ -437,7 +448,8 @@ nm_ndisc_emit_config_change(NMNDisc *self, NMNDiscConfigMap changed)
nm_l3cfg_get_ifindex(priv->config.l3cfg), nm_l3cfg_get_ifindex(priv->config.l3cfg),
rdata, rdata,
priv->config.ip6_privacy, priv->config.ip6_privacy,
priv->iid_is_token ? &priv->iid : NULL); priv->iid_is_token ? &priv->iid : NULL,
priv->config.network_id);
l3cd = nm_l3_config_data_seal(l3cd); l3cd = nm_l3_config_data_seal(l3cd);
if (!nm_l3_config_data_equal(priv->l3cd, l3cd)) if (!nm_l3_config_data_equal(priv->l3cd, l3cd))
@ -760,6 +772,59 @@ nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec
return TRUE; return TRUE;
} }
gboolean
nm_ndisc_add_pref64(NMNDisc *ndisc, const NMNDiscPref64 *new_item, gint64 now_msec)
{
NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
guint i;
guint insert_idx = G_MAXUINT;
for (i = 0; i < rdata->pref64->len;) {
NMNDiscPref64 *item = &nm_g_array_index(rdata->pref64, NMNDiscPref64, i);
if (item->plen == new_item->plen && IN6_ARE_ADDR_EQUAL(&item->prefix, &new_item->prefix)
&& IN6_ARE_ADDR_EQUAL(&item->gateway, &new_item->gateway)) {
if (new_item->expiry_msec <= now_msec) {
g_array_remove_index(rdata->pref64, i);
return TRUE;
}
if (item->gateway_preference != new_item->gateway_preference) {
g_array_remove_index(rdata->pref64, i);
continue;
}
item->gateway_expiry_msec = new_item->gateway_expiry_msec;
if (item->expiry_msec == new_item->expiry_msec)
return FALSE;
item->expiry_msec = new_item->expiry_msec;
return TRUE;
}
/* Put before less preferable gateways. */
if (_preference_to_priority(item->gateway_preference)
< _preference_to_priority(new_item->gateway_preference)
&& insert_idx == G_MAXUINT)
insert_idx = i;
i++;
}
if (rdata->pref64->len >= _SIZE_MAX_PREF64)
return FALSE;
if (new_item->expiry_msec <= now_msec)
return FALSE;
g_array_insert_val(rdata->pref64,
insert_idx == G_MAXUINT ? rdata->pref64->len : insert_idx,
*new_item);
return TRUE;
}
gboolean gboolean
nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec) nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec)
{ {
@ -1400,6 +1465,17 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed)
nm_icmpv6_router_pref_to_string(route->preference, str_pref, sizeof(str_pref)), nm_icmpv6_router_pref_to_string(route->preference, str_pref, sizeof(str_pref)),
get_exp(str_exp, now_msec, route)); get_exp(str_exp, now_msec, route));
} }
for (i = 0; i < rdata->pref64->len; i++) {
const NMNDiscPref64 *pref64 = &nm_g_array_index(rdata->pref64, NMNDiscPref64, i);
char addrstr2[NM_INET_ADDRSTRLEN];
_LOGD(" pref64 %s/%u via %s exp %s",
nm_inet6_ntop(&pref64->prefix, addrstr),
pref64->plen,
nm_inet6_ntop(&pref64->gateway, addrstr2),
get_exp(str_exp, now_msec, pref64));
}
for (i = 0; i < rdata->dns_servers->len; i++) { for (i = 0; i < rdata->dns_servers->len; i++) {
const NMNDiscDNSServer *dns_server = const NMNDiscDNSServer *dns_server =
&nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i); &nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i);
@ -1524,6 +1600,45 @@ clean_routes(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64
*changed |= NM_NDISC_CONFIG_ROUTES; *changed |= NM_NDISC_CONFIG_ROUTES;
} }
static void
clean_pref64(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec)
{
NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
NMNDiscPref64 *arr;
guint i;
guint j;
if (rdata->pref64->len == 0)
return;
arr = &nm_g_array_first(rdata->pref64, NMNDiscPref64);
for (i = 0, j = 0; i < rdata->pref64->len; i++) {
if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)
|| !expiry_next(now_msec,
arr[i].gateway_expiry_msec,
next_msec)) { /* no gateway no party */
if (i == 0) {
/* Emit the changed signal only when the first PREF64 expires,
* because only the first item is exported into the l3cd. Changes
* in other PREF64s are not relevant. */
*changed |= NM_NDISC_CONFIG_PREF64;
}
continue;
}
if (i != j)
arr[j] = arr[i];
j++;
}
if (i != j) {
g_array_set_size(rdata->pref64, j);
}
_array_set_size_max(rdata->pref64, _SIZE_MAX_PREF64);
}
static void static void
clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec) clean_dns_servers(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec)
{ {
@ -1600,6 +1715,7 @@ check_timestamps(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap changed)
clean_gateways(ndisc, now_msec, &changed, &next_msec); clean_gateways(ndisc, now_msec, &changed, &next_msec);
clean_addresses(ndisc, now_msec, &changed, &next_msec); clean_addresses(ndisc, now_msec, &changed, &next_msec);
clean_routes(ndisc, now_msec, &changed, &next_msec); clean_routes(ndisc, now_msec, &changed, &next_msec);
clean_pref64(ndisc, now_msec, &changed, &next_msec);
clean_dns_servers(ndisc, now_msec, &changed, &next_msec); clean_dns_servers(ndisc, now_msec, &changed, &next_msec);
clean_dns_domains(ndisc, now_msec, &changed, &next_msec); clean_dns_domains(ndisc, now_msec, &changed, &next_msec);
@ -1919,6 +2035,7 @@ nm_ndisc_init(NMNDisc *ndisc)
rdata->gateways = g_array_new(FALSE, FALSE, sizeof(NMNDiscGateway)); rdata->gateways = g_array_new(FALSE, FALSE, sizeof(NMNDiscGateway));
rdata->addresses = g_array_new(FALSE, FALSE, sizeof(NMNDiscAddress)); rdata->addresses = g_array_new(FALSE, FALSE, sizeof(NMNDiscAddress));
rdata->routes = g_array_new(FALSE, FALSE, sizeof(NMNDiscRoute)); rdata->routes = g_array_new(FALSE, FALSE, sizeof(NMNDiscRoute));
rdata->pref64 = g_array_new(FALSE, FALSE, sizeof(NMNDiscPref64));
rdata->dns_servers = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSServer)); rdata->dns_servers = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSServer));
rdata->dns_domains = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSDomain)); rdata->dns_domains = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSDomain));
g_array_set_clear_func(rdata->dns_domains, dns_domain_free); g_array_set_clear_func(rdata->dns_domains, dns_domain_free);
@ -1951,6 +2068,7 @@ finalize(GObject *object)
g_array_unref(rdata->gateways); g_array_unref(rdata->gateways);
g_array_unref(rdata->addresses); g_array_unref(rdata->addresses);
g_array_unref(rdata->routes); g_array_unref(rdata->routes);
g_array_unref(rdata->pref64);
g_array_unref(rdata->dns_servers); g_array_unref(rdata->dns_servers);
g_array_unref(rdata->dns_domains); g_array_unref(rdata->dns_domains);

View file

@ -119,6 +119,15 @@ typedef struct _NMNDiscRoute {
bool duplicate : 1; bool duplicate : 1;
} NMNDiscRoute; } NMNDiscRoute;
typedef struct _NMNDiscPref64 {
struct in6_addr prefix;
struct in6_addr gateway;
gint64 expiry_msec;
gint64 gateway_expiry_msec;
NMIcmpv6RouterPref gateway_preference;
guint8 plen;
} NMNDiscPref64;
typedef struct { typedef struct {
struct in6_addr address; struct in6_addr address;
gint64 expiry_msec; gint64 expiry_msec;
@ -141,6 +150,7 @@ typedef enum {
NM_NDISC_CONFIG_MTU = 1 << 7, NM_NDISC_CONFIG_MTU = 1 << 7,
NM_NDISC_CONFIG_REACHABLE_TIME = 1 << 8, NM_NDISC_CONFIG_REACHABLE_TIME = 1 << 8,
NM_NDISC_CONFIG_RETRANS_TIMER = 1 << 9, NM_NDISC_CONFIG_RETRANS_TIMER = 1 << 9,
NM_NDISC_CONFIG_PREF64 = 1 << 10,
} NMNDiscConfigMap; } NMNDiscConfigMap;
typedef enum { typedef enum {
@ -188,12 +198,14 @@ typedef struct {
guint gateways_n; guint gateways_n;
guint addresses_n; guint addresses_n;
guint routes_n; guint routes_n;
guint pref64_n;
guint dns_servers_n; guint dns_servers_n;
guint dns_domains_n; guint dns_domains_n;
const NMNDiscGateway *gateways; const NMNDiscGateway *gateways;
const NMNDiscAddress *addresses; const NMNDiscAddress *addresses;
const NMNDiscRoute *routes; const NMNDiscRoute *routes;
const NMNDiscPref64 *pref64;
const NMNDiscDNSServer *dns_servers; const NMNDiscDNSServer *dns_servers;
const NMNDiscDNSDomain *dns_domains; const NMNDiscDNSDomain *dns_domains;
} NMNDiscData; } NMNDiscData;
@ -282,6 +294,7 @@ struct _NML3ConfigData *nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_id
int ifindex, int ifindex,
const NMNDiscData *rdata, const NMNDiscData *rdata,
NMSettingIP6ConfigPrivacy ip6_privacy, NMSettingIP6ConfigPrivacy ip6_privacy,
NMUtilsIPv6IfaceId *token); NMUtilsIPv6IfaceId *token,
const char *network_id);
#endif /* __NETWORKMANAGER_NDISC_H__ */ #endif /* __NETWORKMANAGER_NDISC_H__ */

View file

@ -39,7 +39,9 @@ typedef struct {
bool activation_lifetime_bound_to_profile_visibility : 1; bool activation_lifetime_bound_to_profile_visibility : 1;
bool settings_connection_is_unsaved : 1; bool settings_connection_is_unsaved : 1;
bool settings_connection_is_shadowed_owned : 1; bool settings_connection_is_shadowed_owned : 1;
bool permanent_managed_by_mac : 1;
NMUnmanFlagOp unmanaged_explicit; NMUnmanFlagOp unmanaged_explicit;
NMTernary permanent_managed;
NMActivationReason activation_reason; NMActivationReason activation_reason;
gulong dev_exported_change_id; gulong dev_exported_change_id;
} DeviceCheckpoint; } DeviceCheckpoint;
@ -160,7 +162,7 @@ parse_connection_from_shadowed_file(const char *path, GError **error)
{ {
nm_auto_unref_keyfile GKeyFile *keyfile = NULL; nm_auto_unref_keyfile GKeyFile *keyfile = NULL;
gs_free char *base_dir = NULL; gs_free char *base_dir = NULL;
char *sep; const char *sep;
keyfile = g_key_file_new(); keyfile = g_key_file_new();
if (!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, error)) if (!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, error))
@ -496,14 +498,19 @@ nm_checkpoint_rollback(NMCheckpoint *self)
/* Start rolling-back each device */ /* Start rolling-back each device */
g_hash_table_iter_init(&iter, priv->devices); g_hash_table_iter_init(&iter, priv->devices);
while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) { while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
guint32 result = NM_ROLLBACK_RESULT_OK; guint32 result = NM_ROLLBACK_RESULT_OK;
NMTernary perm_managed = NM_TERNARY_DEFAULT;
gboolean perm_managed_by_mac = FALSE;
gboolean force_perm_managed;
_LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d, " _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d, "
"connection-unsaved %d, connection-shadowed %d, connection-shadowed-owned %d)", "permanently managed %d, connection-unsaved %d, connection-shadowed %d, "
"connection-shadowed-owned %d)",
dev_checkpoint->original_dev_name, dev_checkpoint->original_dev_name,
(int) dev_checkpoint->state, (int) dev_checkpoint->state,
dev_checkpoint->realized, dev_checkpoint->realized,
dev_checkpoint->unmanaged_explicit, dev_checkpoint->unmanaged_explicit,
dev_checkpoint->permanent_managed,
dev_checkpoint->settings_connection_is_unsaved, dev_checkpoint->settings_connection_is_unsaved,
!!dev_checkpoint->settings_connection_shadowed, !!dev_checkpoint->settings_connection_shadowed,
dev_checkpoint->settings_connection_is_shadowed_owned); dev_checkpoint->settings_connection_is_shadowed_owned);
@ -541,6 +548,43 @@ nm_checkpoint_rollback(NMCheckpoint *self)
NM_DEVICE_STATE_REASON_NOW_MANAGED); NM_DEVICE_STATE_REASON_NOW_MANAGED);
} }
force_perm_managed = !nm_config_get_device_managed(nm_config_get(),
device,
&perm_managed,
&perm_managed_by_mac,
NULL);
if (force_perm_managed || (perm_managed != dev_checkpoint->permanent_managed)
|| (dev_checkpoint->permanent_managed != NM_TERNARY_DEFAULT
&& perm_managed_by_mac != dev_checkpoint->permanent_managed_by_mac)) {
gs_free_error GError *error = NULL;
NMUnmanFlagOp set_op;
_LOGD("rollback: restore permanent managed state");
if (!nm_config_set_device_managed(nm_config_get(),
device,
dev_checkpoint->permanent_managed,
dev_checkpoint->permanent_managed_by_mac,
&error)) {
_LOGE("rollback: failed to restore permanent managed state: %s", error->message);
result = NM_ROLLBACK_RESULT_ERR_FAILED;
/* even if this failed, we try to continue the rollback */
}
if (dev_checkpoint->permanent_managed == NM_TERNARY_TRUE)
set_op = NM_UNMAN_FLAG_OP_SET_MANAGED;
else if (dev_checkpoint->permanent_managed == NM_TERNARY_FALSE)
set_op = NM_UNMAN_FLAG_OP_SET_UNMANAGED;
else
set_op = NM_UNMAN_FLAG_OP_FORGET;
nm_device_set_unmanaged_by_flags_queue(device,
NM_UNMANAGED_USER_CONF,
set_op,
NM_DEVICE_STATE_REASON_NOW_MANAGED);
}
if (dev_checkpoint->state == NM_DEVICE_STATE_UNMANAGED) { if (dev_checkpoint->state == NM_DEVICE_STATE_UNMANAGED) {
if (nm_device_get_state(device) != NM_DEVICE_STATE_UNMANAGED if (nm_device_get_state(device) != NM_DEVICE_STATE_UNMANAGED
|| dev_checkpoint->unmanaged_explicit == NM_UNMAN_FLAG_OP_SET_UNMANAGED) { || dev_checkpoint->unmanaged_explicit == NM_UNMAN_FLAG_OP_SET_UNMANAGED) {
@ -703,6 +747,8 @@ device_checkpoint_create(NMCheckpoint *self, NMDevice *device)
NMSettingsConnection *settings_connection; NMSettingsConnection *settings_connection;
const char *path; const char *path;
NMActRequest *act_request; NMActRequest *act_request;
gboolean perm_managed_by_mac;
gs_free_error GError *error = NULL;
nm_assert(NM_IS_DEVICE(device)); nm_assert(NM_IS_DEVICE(device));
nm_assert(nm_device_is_real(device)); nm_assert(nm_device_is_real(device));
@ -728,12 +774,26 @@ device_checkpoint_create(NMCheckpoint *self, NMDevice *device)
} else } else
dev_checkpoint->unmanaged_explicit = NM_UNMAN_FLAG_OP_FORGET; dev_checkpoint->unmanaged_explicit = NM_UNMAN_FLAG_OP_FORGET;
if (nm_config_get_device_managed(nm_config_get(),
device,
&dev_checkpoint->permanent_managed,
&perm_managed_by_mac,
NULL)) {
dev_checkpoint->permanent_managed_by_mac = perm_managed_by_mac;
} else {
dev_checkpoint->permanent_managed = NM_TERNARY_DEFAULT;
dev_checkpoint->permanent_managed_by_mac = FALSE;
_LOGW("error getting permanent managed state for %s: %s",
nm_device_get_iface(device),
error->message);
g_clear_error(&error);
}
act_request = nm_device_get_act_request(device); act_request = nm_device_get_act_request(device);
if (act_request) { if (act_request) {
NMSettingsStorage *storage; NMSettingsStorage *storage;
gboolean shadowed_owned = FALSE; gboolean shadowed_owned = FALSE;
const char *shadowed_file; const char *shadowed_file;
gs_free_error GError *error = NULL;
settings_connection = nm_act_request_get_settings_connection(act_request); settings_connection = nm_act_request_get_settings_connection(act_request);
applied_connection = nm_act_request_get_applied_connection(act_request); applied_connection = nm_act_request_get_applied_connection(act_request);
@ -764,6 +824,7 @@ device_checkpoint_create(NMCheckpoint *self, NMDevice *device)
_LOGW("error reading shadowed connection file for %s: %s", _LOGW("error reading shadowed connection file for %s: %s",
nm_device_get_iface(device), nm_device_get_iface(device),
error->message); error->message);
g_clear_error(&error);
} }
} }
} }

View file

@ -2058,12 +2058,15 @@ _match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
{ {
char **groups; char **groups;
gsize i, j, ngroups; gsize i, j, ngroups;
char *connection_tag = NULL; char *main_group = NULL;
MatchSectionInfo *match_section_infos = NULL; MatchSectionInfo *match_section_infos = NULL;
const char *prefix; const char *prefix, *prefix_intern;
prefix = prefix =
is_device ? NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE : NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION; is_device ? NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE : NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION;
prefix_intern =
is_device ? NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE
: NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION;
/* get the list of existing [connection.\+]/[device.\+] sections. /* get the list of existing [connection.\+]/[device.\+] sections.
* *
@ -2074,27 +2077,36 @@ _match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
if (!groups) if (!groups)
return NULL; return NULL;
if (ngroups > 0) { for (i = 0, j = 0; i < ngroups; i++) {
gsize l = strlen(prefix); if (nm_streq0(groups[i], prefix)) {
main_group = groups[i];
for (i = 0, j = 0; i < ngroups; i++) { } else if (nm_streq0(groups[i], prefix_intern)) {
if (g_str_has_prefix(groups[i], prefix)) { /* [.intern.connection] and [.intern.device] should not exist */
if (groups[i][l] == '\0') _nm_log(LOGL_WARN,
connection_tag = groups[i]; LOGD_CORE,
else 0,
groups[j++] = groups[i]; NULL,
} else NULL,
g_free(groups[i]); "Invalid [.intern.*] section 'connection' or 'device' found");
g_free(groups[i]);
continue;
} else if (g_str_has_prefix(groups[i], prefix)) {
groups[j++] = groups[i];
} else if (g_str_has_prefix(groups[i], prefix_intern)) {
/* [.intern.connection-whatever] and [.intern.device-whatever] can exist */
groups[j++] = groups[i];
} else {
g_free(groups[i]);
} }
ngroups = j;
} }
ngroups = j;
if (ngroups == 0 && !connection_tag) { if (ngroups == 0 && !main_group) {
g_free(groups); g_free(groups);
return NULL; return NULL;
} }
match_section_infos = g_new0(MatchSectionInfo, ngroups + 1 + (connection_tag ? 1 : 0)); match_section_infos = g_new0(MatchSectionInfo, ngroups + 1 + (main_group ? 1 : 0));
match_section_infos->is_device = is_device; match_section_infos->is_device = is_device;
for (i = 0; i < ngroups; i++) { for (i = 0; i < ngroups; i++) {
/* pass ownership of @group on... */ /* pass ownership of @group on... */
@ -2103,9 +2115,9 @@ _match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
groups[ngroups - i - 1], groups[ngroups - i - 1],
is_device); is_device);
} }
if (connection_tag) { if (main_group) {
/* pass ownership of @connection_tag on... */ /* pass ownership of @main_group on... */
_match_section_info_init(&match_section_infos[i], keyfile, connection_tag, is_device); _match_section_info_init(&match_section_infos[i], keyfile, main_group, is_device);
} }
g_free(groups); g_free(groups);

View file

@ -892,6 +892,7 @@ static const ConfigGroup config_groups[] = {
.is_prefix = TRUE, .is_prefix = TRUE,
.keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT, .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT,
NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER,
NM_CONFIG_KEYFILE_KEY_DEVICE_CHECK_CONNECTIVITY,
NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS,
NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION, NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION,
@ -2075,6 +2076,210 @@ nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled)
g_key_file_unref(keyfile); g_key_file_unref(keyfile);
} }
/*****************************************************************************/
static gboolean
normalize_hwaddr_for_group_name(const char *hwaddr, char *out, GError **error)
{
guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
gsize hwaddr_bin_len;
if (!_nm_utils_hwaddr_aton(hwaddr, hwaddr_bin, sizeof(hwaddr_bin), &hwaddr_bin_len)) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"Invalid MAC address: %s",
hwaddr);
return FALSE;
}
nm_utils_bin2hexstr_full(hwaddr_bin, hwaddr_bin_len, '-', TRUE, out);
return TRUE;
}
/**
* nm_config_get_device_managed:
* @self: the NMConfig instance
* @device: the interface
* @out: (out): the managed state of the device
* @error: return location for a #GError, or %NULL
*
* Returns: TRUE if there were no errors, FALSE otherwise.
*/
gboolean
nm_config_get_device_managed(NMConfig *self,
NMDevice *device,
NMTernary *out_managed,
gboolean *out_by_mac,
GError **error)
{
NMConfigPrivate *priv;
const GKeyFile *keyfile = NULL;
gs_free char *group_by_name = NULL;
gs_free char *group_by_mac = NULL;
const char *ifname = nm_device_get_iface(device);
const char *hwaddr = nm_device_get_permanent_hw_address(device);
char mac_group_name[NM_UTILS_HWADDR_LEN_MAX * 3 + 1];
NMTernary val_by_name, val_by_mac = NM_TERNARY_DEFAULT;
g_return_val_if_fail(NM_IS_CONFIG(self), FALSE);
g_return_val_if_fail(NM_CONFIG_GET_PRIVATE(self)->config_data, FALSE);
g_return_val_if_fail(out_managed, FALSE);
g_return_val_if_fail(ifname, FALSE);
priv = NM_CONFIG_GET_PRIVATE(self);
keyfile = _nm_config_data_get_keyfile_intern(priv->config_data);
if (!keyfile) {
NM_SET_OUT(out_managed, NM_TERNARY_DEFAULT);
NM_SET_OUT(out_by_mac, FALSE);
return TRUE;
}
group_by_name =
g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s", ifname);
val_by_name = (NMTernary) nm_config_keyfile_get_boolean(keyfile,
group_by_name,
NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
NM_TERNARY_DEFAULT);
/* Devices without a kernel link (i.e. OVS ports) don't have a MAC address */
if (hwaddr) {
if (!normalize_hwaddr_for_group_name(hwaddr, mac_group_name, error))
return FALSE;
group_by_mac = g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s",
mac_group_name);
val_by_mac = (NMTernary) nm_config_keyfile_get_boolean(keyfile,
group_by_mac,
NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
NM_TERNARY_DEFAULT);
}
if (val_by_name != NM_TERNARY_DEFAULT && val_by_mac == NM_TERNARY_DEFAULT) {
NM_SET_OUT(out_managed, val_by_name);
NM_SET_OUT(out_by_mac, FALSE);
return TRUE;
} else if (val_by_mac != NM_TERNARY_DEFAULT && val_by_name == NM_TERNARY_DEFAULT) {
NM_SET_OUT(out_managed, val_by_mac);
NM_SET_OUT(out_by_mac, TRUE);
return TRUE;
} else if (val_by_name == NM_TERNARY_DEFAULT && val_by_mac == NM_TERNARY_DEFAULT) {
NM_SET_OUT(out_managed, NM_TERNARY_DEFAULT);
NM_SET_OUT(out_by_mac, FALSE);
return TRUE;
} else if (val_by_name == val_by_mac) {
NM_SET_OUT(out_managed, val_by_name);
NM_SET_OUT(out_by_mac, FALSE);
return TRUE;
}
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
"Multiple managed states found for device: %s",
nm_device_get_iface(device));
return FALSE;
}
/**
* nm_config_set_device_managed:
* @self: the NMConfig instance
* @device: the NMDevice instance associated with this config change
* @managed: the managed state to set
* @by_mac: if %TRUE, match by MAC address, otherwise by interface name. This is
* only used when @managed = TRUE.
* @error: return location for a #GError, or %NULL
*
* Sets the managed state of the device to the intern keyfile. Here we store the
* configuration received via the D-Bus API. Configurations from other config
* files are still in place and may have higher precedence.
*
* Prior to setting the new state, the existing configuration is removed. If
* @managed is set to %NM_TERNARY_DEFAULT, we only do the removal of the previous
* configuration.
*/
gboolean
nm_config_set_device_managed(NMConfig *self,
NMDevice *device,
NMTernary managed,
gboolean by_mac,
GError **error)
{
NMConfigPrivate *priv;
g_autoptr(GKeyFile) keyfile = NULL;
char *group;
gs_free char *group_by_name = NULL;
gs_free char *group_by_mac = NULL;
gs_free char *match_value = NULL;
gboolean changed = FALSE;
const char *ifname = nm_device_get_iface(device);
const char *hwaddr = nm_device_get_permanent_hw_address(device);
char mac_group_name[NM_UTILS_HWADDR_LEN_MAX * 3 + 1];
g_return_val_if_fail(NM_IS_CONFIG(self), FALSE);
g_return_val_if_fail(NM_CONFIG_GET_PRIVATE(self)->config_data, FALSE);
g_return_val_if_fail(ifname, FALSE);
if (by_mac && !hwaddr) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"the device has no MAC address, but match by MAC was requested");
return FALSE;
}
if (hwaddr && !normalize_hwaddr_for_group_name(hwaddr, mac_group_name, error))
return FALSE;
priv = NM_CONFIG_GET_PRIVATE(self);
keyfile = nm_config_data_clone_keyfile_intern(priv->config_data);
/* Remove existing configs. Search them by group name [.intern.device-manage-*]. In
* the intern file, 'device-manage' sections are only used for this purpose, so we
* won't remove any other device's config. */
group_by_name =
g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s", ifname);
if (g_key_file_remove_group(keyfile, group_by_name, NULL))
changed = TRUE;
/* Devices without a kernel link (i.e. OVS ports) don't have a MAC address */
if (hwaddr) {
group_by_mac = g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s",
mac_group_name);
if (g_key_file_remove_group(keyfile, group_by_mac, NULL))
changed = TRUE;
}
/* If the new state is not explicitly TRUE of FALSE, we only remove the configs */
if (managed == NM_TERNARY_DEFAULT)
goto done;
/* Set new values */
if (by_mac) {
group = group_by_mac;
match_value = g_strdup_printf("mac:%s", hwaddr);
} else {
group = group_by_name;
match_value = g_strdup_printf("interface-name:=%s", ifname);
}
g_key_file_set_value(keyfile, group, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, match_value);
g_key_file_set_value(keyfile, group, NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, managed ? "1" : "0");
changed = TRUE;
done:
if (changed)
nm_config_set_values(self, keyfile, TRUE, FALSE);
return TRUE;
}
/*****************************************************************************/
/** /**
* nm_config_set_values: * nm_config_set_values:
* @self: the NMConfig instance * @self: the NMConfig instance

View file

@ -142,6 +142,17 @@ gboolean nm_config_set_global_dns(NMConfig *self, NMGlobalDnsConfig *global_dns,
void nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled); void nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled);
gboolean nm_config_get_device_managed(NMConfig *self,
NMDevice *device,
NMTernary *out_managed,
gboolean *out_by_mac,
GError **error);
gboolean nm_config_set_device_managed(NMConfig *self,
NMDevice *device,
NMTernary managed,
gboolean by_mac,
GError **error);
/* internal defines ... */ /* internal defines ... */
extern guint _nm_config_match_nm_version; extern guint _nm_config_match_nm_version;
extern char *_nm_config_match_env; extern char *_nm_config_match_env;

View file

@ -21,6 +21,10 @@
#include <linux/if_infiniband.h> #include <linux/if_infiniband.h>
#include <net/if_arp.h> #include <net/if_arp.h>
#include <net/ethernet.h> #include <net/ethernet.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <linux/if_packet.h>
#include "libnm-glib-aux/nm-uuid.h" #include "libnm-glib-aux/nm-uuid.h"
#include "libnm-platform/nmp-base.h" #include "libnm-platform/nmp-base.h"
@ -5002,6 +5006,469 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_activation_type_to_string,
/*****************************************************************************/ /*****************************************************************************/
typedef struct {
NMIPAddrTyped address;
char *addr_str;
GTask *task;
GSource *timeout_source;
GSource *retry_source;
GSource *input_source;
gulong cancellable_id;
int ifindex;
int socket;
guint16 seq;
} PingInfo;
#define _NMLOG2_PREFIX_NAME "ping"
#define _NMLOG2_DOMAIN LOGD_CORE
#define _NMLOG2(level, info, ...) \
G_STMT_START \
{ \
if (nm_logging_enabled((level), (_NMLOG2_DOMAIN))) { \
PingInfo *_info = (info); \
\
_nm_log((level), \
(_NMLOG2_DOMAIN), \
0, \
NULL, \
NULL, \
_NMLOG2_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \
",if=%d,%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
NM_HASH_OBFUSCATE_PTR(_info), \
_info->ifindex, \
_info->addr_str _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} \
G_STMT_END
static void
ping_complete(PingInfo *info, GError *error)
{
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
if (error && !nm_utils_error_is_cancelled(error)) {
_LOG2T(info, "terminated with error: %s", error->message);
}
if (error)
g_task_return_error(info->task, error);
else
g_task_return_boolean(info->task, TRUE);
nm_clear_g_source_inst(&info->timeout_source);
nm_clear_g_source_inst(&info->retry_source);
nm_clear_g_source_inst(&info->input_source);
nm_clear_g_free(&info->addr_str);
nm_clear_fd(&info->socket);
g_object_unref(info->task);
g_free(info);
}
static gboolean
ping_socket_data_cb(int fd, GIOCondition condition, gpointer user_data)
{
PingInfo *info = user_data;
ssize_t len;
union {
struct icmphdr icmph;
struct icmp6_hdr icmp6h;
} pkt;
len = recv(fd, &pkt, sizeof(pkt), 0);
if (len < 0)
return G_SOURCE_CONTINUE;
if (info->address.addr_family == AF_INET) {
if (len >= sizeof(struct icmphdr) && pkt.icmph.type == ICMP_ECHOREPLY) {
_LOG2T(info, "received echo-reply with seq %hu", ntohs(pkt.icmph.un.echo.sequence));
ping_complete(info, NULL);
return G_SOURCE_CONTINUE;
}
} else {
if (len >= sizeof(struct icmp6_hdr) && pkt.icmp6h.icmp6_type == ICMP6_ECHO_REPLY) {
_LOG2T(info, "received echo-reply with seq %hu", ntohs(pkt.icmp6h.icmp6_seq));
ping_complete(info, NULL);
return G_SOURCE_CONTINUE;
}
}
return G_SOURCE_CONTINUE;
}
static void
ping_send(PingInfo *info)
{
const bool IS_IPv4 = NM_IS_IPv4(info->address.addr_family);
union {
struct sockaddr_in6 sa6;
struct sockaddr_in sa4;
} sa;
union {
struct icmphdr icmph;
struct icmp6_hdr icmp6h;
} pkt;
socklen_t sa_len;
size_t pkt_len;
nm_be32_t ifindex_be;
int errsv;
info->seq++;
if (info->socket < 0) {
info->socket = socket(info->address.addr_family,
SOCK_DGRAM | SOCK_CLOEXEC,
IS_IPv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6);
if (info->socket < 0) {
errsv = errno;
_LOG2T(info, "socket creation failed: %s", nm_strerror_native(errsv));
/* Try again at the next iteration */
return;
}
memset(&sa, 0, sizeof(sa));
if (IS_IPv4) {
sa.sa4.sin_family = AF_INET;
sa.sa4.sin_addr.s_addr = info->address.addr.addr4;
sa_len = sizeof(struct sockaddr_in);
} else {
sa.sa6.sin6_family = AF_INET6;
sa.sa6.sin6_addr = info->address.addr.addr6;
if (IN6_IS_ADDR_LINKLOCAL(&info->address.addr.addr6))
sa.sa6.sin6_scope_id = info->ifindex;
sa_len = sizeof(struct sockaddr_in6);
}
/* setsockopt(IP*_UNICAST_IF) must be called *before* connecting
* the socket, otherwise it doesn't have any effect */
ifindex_be = htonl(info->ifindex);
if (setsockopt(info->socket,
IS_IPv4 ? IPPROTO_IP : IPPROTO_IPV6,
IS_IPv4 ? IP_UNICAST_IF : IPV6_UNICAST_IF,
&ifindex_be,
sizeof(ifindex_be))) {
errsv = errno;
_LOG2T(info,
"failed to bind the socket to the interface: %s",
nm_strerror_native(errsv));
/* Try again at the next iteration */
nm_clear_fd(&info->socket);
return;
}
/* Connect the socket so that the kernel only delivers us packets
* coming from the given remote address */
if (connect(info->socket, (struct sockaddr *) &sa, sa_len) < 0) {
errsv = errno;
_LOG2T(info, "failed to connect the socket: %s", nm_strerror_native(errsv));
/* try again at the next iteration */
nm_clear_fd(&info->socket);
return;
}
info->input_source = nm_g_unix_fd_source_new(info->socket,
G_IO_IN,
G_PRIORITY_DEFAULT,
ping_socket_data_cb,
info,
NULL);
g_source_attach(info->input_source, g_task_get_context(info->task));
}
if (IS_IPv4) {
memset(&pkt.icmph, 0, sizeof(struct icmphdr));
pkt.icmph.type = ICMP_ECHO;
pkt.icmph.un.echo.sequence = htons(info->seq);
pkt_len = sizeof(struct icmphdr);
} else {
memset(&pkt.icmp6h, 0, sizeof(struct icmp6_hdr));
pkt.icmp6h.icmp6_type = ICMP6_ECHO_REQUEST;
pkt.icmp6h.icmp6_seq = htons(info->seq);
pkt_len = sizeof(struct icmp6_hdr);
}
/* The kernel will automatically set the ID ICMP field and filter
* incoming packets by the same ID */
if (send(info->socket, &pkt, pkt_len, 0) < 0) {
errsv = errno;
_LOG2T(info, "error sending echo-request #%u: %s", info->seq, nm_strerror_native(errsv));
return;
}
_LOG2T(info, "sent echo-request #%u", info->seq);
}
static gboolean
ping_timeout_cb(gpointer user_data)
{
PingInfo *info = user_data;
_LOG2T(info, "timeout");
nm_clear_g_source_inst(&info->timeout_source);
ping_complete(info, g_error_new_literal(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "timeout"));
return G_SOURCE_CONTINUE;
}
static gboolean
ping_retry_cb(gpointer user_data)
{
PingInfo *info = user_data;
ping_send(info);
return G_SOURCE_CONTINUE;
}
static void
ping_cancelled(GObject *object, gpointer user_data)
{
PingInfo *info = user_data;
GError *error = NULL;
nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
nm_utils_error_set_cancelled(&error, FALSE, NULL);
ping_complete(info, error);
}
void
nm_utils_ping_host(NMIPAddrTyped address,
int ifindex,
guint timeout_sec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer cb_data)
{
PingInfo *info;
char buf[NM_INET_ADDRSTRLEN];
gulong signal_id;
nm_assert(ifindex > 0);
nm_assert(G_IS_CANCELLABLE(cancellable));
nm_assert(callback);
nm_assert(cb_data);
info = g_new0(PingInfo, 1);
info->address = address;
info->ifindex = ifindex;
info->task = nm_g_task_new(NULL, cancellable, nm_utils_ping_host, callback, cb_data);
info->socket = -1;
nm_inet_ntop(address.addr_family, address.addr.addr_ptr, buf);
info->addr_str = g_strdup(buf);
_LOG2T(info, "started");
if (timeout_sec > 0) {
info->timeout_source = nm_g_timeout_source_new_seconds(timeout_sec,
G_PRIORITY_DEFAULT,
ping_timeout_cb,
info,
NULL);
g_source_attach(info->timeout_source, g_task_get_context(info->task));
}
info->retry_source =
nm_g_timeout_source_new_seconds(1, G_PRIORITY_DEFAULT, ping_retry_cb, info, NULL);
g_source_attach(info->retry_source, g_task_get_context(info->task));
signal_id = g_cancellable_connect(cancellable, G_CALLBACK(ping_cancelled), info, NULL);
if (signal_id == 0) {
/* the callback was invoked synchronously, which destroyed @info.
* We must not touch it anymore. */
return;
}
info->cancellable_id = signal_id;
ping_send(info);
}
gboolean
nm_utils_ping_host_finish(GAsyncResult *result, GError **error)
{
GTask *task = G_TASK(result);
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_ping_host));
return g_task_propagate_boolean(task, error);
}
/*****************************************************************************/
/*
* nm_utils_icmp6_checksum:
* @ip6_src: pointer to the IPv6 source address
* @data_len: length of the data
* @data: the data on which to compute the checksum
*
* Computes the ICMP6 checksum over @data (with length @data_len) and the IPv6
* pseudo-header. @ip6_src points to the source address in the IPv6 header.
*/
uint16_t
nm_utils_icmp6_checksum(const void *ip6_src, size_t data_len, const void *data)
{
uint32_t sum = 0;
const uint16_t *ptr;
const uint8_t *ptr8;
size_t i;
/* Pseudo-header: source address */
ptr = (const uint16_t *) ip6_src;
for (i = 0; i < 8; i++)
sum += *ptr++;
/* Pseudo-header: destination address */
for (i = 0; i < 8; i++)
sum += *ptr++;
/* Pseudo-header: payload length */
sum += htons(data_len);
/* Pseudo-header: next header */
sum += htons(IPPROTO_ICMPV6);
/* ICMPv6 data */
ptr = (const uint16_t *) data;
for (i = 0; i < data_len / 2; i++)
sum += ptr[i];
/* Handle odd byte */
if (data_len % 2) {
ptr8 = &((const uint8_t *) data)[data_len - 1];
sum += htons((guint16) (*ptr8) << 8);
}
/* Fold 32-bit sum to 16 bits */
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t) ~sum;
}
/*
* nm_utils_ipv6_dad_send:
* @addr: the target IPv6 address
* @ifindex: the interface index
* @arptype: the ARP hardware type of the interface (e.g. ARPHRD_ETHER, ARPHRD_NONE)
*
* Send an IPv6 Duplicate Address Detection (DAD) Neighbor Solicitation
* for the given address.
*
* Returns: %TRUE if the packet was sent successfully, %FALSE on error
*/
gboolean
nm_utils_ipv6_dad_send(const struct in6_addr *addr, int ifindex, int arptype)
{
/* DAD packet: IPv6 header + ICMPv6 NS + nonce option (RFC 3971) */
struct _nm_packed {
struct ip6_hdr ip6h;
struct nd_neighbor_solicit ns;
guint8 ns_opt_nr;
guint8 ns_opt_len;
guint8 ns_opt_nonce[6];
} dad_pkt;
nm_auto_close int fd = -1;
int errsv;
char sbuf[NM_INET_ADDRSTRLEN];
nm_assert(addr);
nm_assert(ifindex > 0);
/* IPv6 header */
dad_pkt.ip6h = (struct ip6_hdr) {
.ip6_flow = htonl(6 << 28), /* version 6, tclass 0, flowlabel 0 */
.ip6_plen = htons(sizeof(dad_pkt) - sizeof(struct ip6_hdr)),
.ip6_nxt = IPPROTO_ICMPV6,
.ip6_hlim = 255,
.ip6_src = IN6ADDR_ANY_INIT,
.ip6_dst.s6_addr = {0xff,
0x02,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0xff,
addr->s6_addr[13],
addr->s6_addr[14],
addr->s6_addr[15]},
};
/* ICMPv6 Neighbor Solicitation */
dad_pkt.ns = (struct nd_neighbor_solicit) {
.nd_ns_type = ND_NEIGHBOR_SOLICIT,
.nd_ns_target = *addr,
};
/* Nonce option (RFC 3971) */
dad_pkt.ns_opt_nr = 14;
dad_pkt.ns_opt_len = 1; /* in units of 8 bytes */
nm_random_get_bytes(dad_pkt.ns_opt_nonce, sizeof(dad_pkt.ns_opt_nonce));
/* Compute the ICMPv6 checksum */
dad_pkt.ns.nd_ns_cksum = nm_utils_icmp6_checksum(&dad_pkt.ip6h.ip6_src,
sizeof(dad_pkt) - sizeof(struct ip6_hdr),
&dad_pkt.ns);
/* We need a ETH_P_IPV6 socket because we need to use a zero IPv6 source address */
fd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
if (fd < 0) {
errsv = errno;
nm_log_warn(LOGD_CORE,
"ipv6-dad: failed to create socket for %s: %s",
nm_inet6_ntop(addr, sbuf),
nm_strerror_native(errsv));
return FALSE;
}
/* Build link-layer destination address. For Ethernet, use the solicited-node
* multicast MAC address. For L3-only devices (ARPHRD_NONE, ARPHRD_RAWIP, etc.)
* there is no L2 header, so set sll_halen to 0. */
{
struct sockaddr_ll dst_ll = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IPV6),
.sll_ifindex = ifindex,
};
if (arptype == ARPHRD_ETHER) {
dst_ll.sll_halen = ETH_ALEN;
dst_ll.sll_addr[0] = 0x33;
dst_ll.sll_addr[1] = 0x33;
dst_ll.sll_addr[2] = 0xff;
dst_ll.sll_addr[3] = addr->s6_addr[13];
dst_ll.sll_addr[4] = addr->s6_addr[14];
dst_ll.sll_addr[5] = addr->s6_addr[15];
}
if (sendto(fd, &dad_pkt, sizeof(dad_pkt), 0, (struct sockaddr *) &dst_ll, sizeof(dst_ll))
< 0) {
errsv = errno;
nm_log_warn(LOGD_CORE,
"ipv6-dad: failed to send DAD NS for %s: %s",
nm_inet6_ntop(addr, sbuf),
nm_strerror_native(errsv));
return FALSE;
}
}
nm_log_dbg(LOGD_CORE,
"ipv6-dad: sent DAD NS for %s on ifindex %d",
nm_inet6_ntop(addr, sbuf),
ifindex);
return TRUE;
}
/*****************************************************************************/
typedef struct { typedef struct {
GPid pid; GPid pid;
GTask *task; GTask *task;
@ -5023,6 +5490,9 @@ typedef struct {
gsize out_buffer_offset; gsize out_buffer_offset;
} HelperInfo; } HelperInfo;
#undef _NMLOG2_PREFIX_NAME
#undef _NMLOG2_DOMAIN
#undef _NMLOG2
#define _NMLOG2_PREFIX_NAME "nm-daemon-helper" #define _NMLOG2_PREFIX_NAME "nm-daemon-helper"
#define _NMLOG2_DOMAIN LOGD_CORE #define _NMLOG2_DOMAIN LOGD_CORE
#define _NMLOG2(level, info, ...) \ #define _NMLOG2(level, info, ...) \

View file

@ -304,6 +304,7 @@ typedef enum {
NM_UTILS_STABLE_TYPE_STABLE_ID = 1, NM_UTILS_STABLE_TYPE_STABLE_ID = 1,
NM_UTILS_STABLE_TYPE_GENERATED = 2, NM_UTILS_STABLE_TYPE_GENERATED = 2,
NM_UTILS_STABLE_TYPE_RANDOM = 3, NM_UTILS_STABLE_TYPE_RANDOM = 3,
NM_UTILS_STABLE_TYPE_CLAT = 4,
} NMUtilsStableType; } NMUtilsStableType;
#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) - 1) #define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) - 1)
@ -520,4 +521,19 @@ void nm_utils_read_private_files(const char *const *paths,
gpointer cb_data); gpointer cb_data);
GHashTable *nm_utils_read_private_files_finish(GAsyncResult *result, GError **error); GHashTable *nm_utils_read_private_files_finish(GAsyncResult *result, GError **error);
/*****************************************************************************/
void nm_utils_ping_host(NMIPAddrTyped address,
int ifindex,
guint timeout_sec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer cb_data);
gboolean nm_utils_ping_host_finish(GAsyncResult *result, GError **error);
uint16_t nm_utils_icmp6_checksum(const void *ip6_src, size_t data_len, const void *data);
gboolean nm_utils_ipv6_dad_send(const struct in6_addr *addr, int ifindex, int arptype);
#endif /* __NM_CORE_UTILS_H__ */ #endif /* __NM_CORE_UTILS_H__ */

View file

@ -26,6 +26,7 @@ GType nm_ip6_config_get_type(void);
/*****************************************************************************/ /*****************************************************************************/
#define NM_IP_CONFIG_ADDRESS_DATA "address-data" #define NM_IP_CONFIG_ADDRESS_DATA "address-data"
#define NM_IP_CONFIG_CLAT_ADDRESS "clat-address"
#define NM_IP_CONFIG_DNS_OPTIONS "dns-options" #define NM_IP_CONFIG_DNS_OPTIONS "dns-options"
#define NM_IP_CONFIG_DNS_PRIORITY "dns-priority" #define NM_IP_CONFIG_DNS_PRIORITY "dns-priority"
#define NM_IP_CONFIG_DOMAINS "domains" #define NM_IP_CONFIG_DOMAINS "domains"
@ -41,6 +42,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_FULL(_ip,
NMIPConfig, NMIPConfig,
PROP_IP_L3CFG, PROP_IP_L3CFG,
PROP_IP_ADDRESS_DATA, PROP_IP_ADDRESS_DATA,
PROP_IP_CLAT_ADDRESS,
PROP_IP_GATEWAY, PROP_IP_GATEWAY,
PROP_IP_ROUTE_DATA, PROP_IP_ROUTE_DATA,
PROP_IP_DOMAINS, PROP_IP_DOMAINS,
@ -54,7 +56,7 @@ G_DEFINE_ABSTRACT_TYPE(NMIPConfig, nm_ip_config, NM_TYPE_DBUS_OBJECT)
/*****************************************************************************/ /*****************************************************************************/
static void _handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_init); static void _handle_platform_change(NMIPConfig *self, guint64 obj_type_flags, gboolean is_init);
static void _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd); static void _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd);
/*****************************************************************************/ /*****************************************************************************/
@ -75,7 +77,7 @@ static void
_notify_platform_handle(NMIPConfig *self, gint64 now_msec) _notify_platform_handle(NMIPConfig *self, gint64 now_msec)
{ {
NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self);
guint32 obj_type_flags; guint64 obj_type_flags;
nm_clear_g_source_inst(&priv->notify_platform_timeout_source); nm_clear_g_source_inst(&priv->notify_platform_timeout_source);
@ -96,7 +98,7 @@ _notify_platform_cb(gpointer user_data)
} }
static void static void
_notify_platform(NMIPConfig *self, guint32 obj_type_flags) _notify_platform(NMIPConfig *self, guint64 obj_type_flags)
{ {
const int addr_family = nm_ip_config_get_addr_family(self); const int addr_family = nm_ip_config_get_addr_family(self);
const int IS_IPv4 = NM_IS_IPv4(addr_family); const int IS_IPv4 = NM_IS_IPv4(addr_family);
@ -164,6 +166,8 @@ get_property_ip(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec
const int addr_family = nm_ip_config_get_addr_family(self); const int addr_family = nm_ip_config_get_addr_family(self);
char **to_free = NULL; char **to_free = NULL;
char sbuf_addr[NM_INET_ADDRSTRLEN]; char sbuf_addr[NM_INET_ADDRSTRLEN];
in_addr_t addr4;
struct in6_addr addr6;
const char *const *strv; const char *const *strv;
guint len; guint len;
int v_i; int v_i;
@ -218,6 +222,20 @@ get_property_ip(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec
strv = nm_l3_config_data_get_dns_options(priv->l3cd, addr_family, &len); strv = nm_l3_config_data_get_dns_options(priv->l3cd, addr_family, &len);
_value_set_variant_as(value, strv, len); _value_set_variant_as(value, strv, len);
break; break;
case PROP_IP_CLAT_ADDRESS:
if (nm_l3_config_data_get_clat_state(priv->l3cd, &addr6, NULL, NULL, &addr4)) {
if (addr_family == AF_INET) {
g_value_set_variant(value,
g_variant_new_string(nm_inet_ntop(AF_INET, &addr4, sbuf_addr)));
} else {
g_value_set_variant(
value,
g_variant_new_string(nm_inet_ntop(AF_INET6, &addr6, sbuf_addr)));
}
} else {
g_value_set_variant(value, nm_g_variant_singleton_s_empty());
}
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break; break;
@ -336,6 +354,13 @@ nm_ip_config_class_init(NMIPConfigClass *klass)
G_VARIANT_TYPE("aa{sv}"), G_VARIANT_TYPE("aa{sv}"),
NULL, NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties_ip[PROP_IP_CLAT_ADDRESS] =
g_param_spec_variant(NM_IP_CONFIG_CLAT_ADDRESS,
"",
"",
G_VARIANT_TYPE("s"),
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties_ip[PROP_IP_GATEWAY] = obj_properties_ip[PROP_IP_GATEWAY] =
g_param_spec_variant(NM_IP_CONFIG_GATEWAY, g_param_spec_variant(NM_IP_CONFIG_GATEWAY,
"", "",
@ -512,6 +537,9 @@ static const NMDBusInterfaceInfoExtended interface_info_ip4_config = {
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData", NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData",
"aa{sv}", "aa{sv}",
NM_IP_CONFIG_ADDRESS_DATA), NM_IP_CONFIG_ADDRESS_DATA),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ClatAddress",
"s",
NM_IP_CONFIG_CLAT_ADDRESS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP_CONFIG_GATEWAY), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP_CONFIG_GATEWAY),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE( NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
"Routes", "Routes",
@ -614,6 +642,7 @@ nm_ip4_config_class_init(NMIP4ConfigClass *klass)
/*****************************************************************************/ /*****************************************************************************/
/* public */ /* public */
#define NM_IP6_CONFIG_CLAT_PREF64 "clat-pref64"
#define NM_IP6_CONFIG_NAMESERVERS "nameservers" #define NM_IP6_CONFIG_NAMESERVERS "nameservers"
/* deprecated */ /* deprecated */
@ -625,6 +654,7 @@ typedef struct _NMIP6ConfigClass NMIP6ConfigClass;
NM_GOBJECT_PROPERTIES_DEFINE_FULL(_ip6, NM_GOBJECT_PROPERTIES_DEFINE_FULL(_ip6,
NMIP6Config, NMIP6Config,
PROP_IP6_CLAT_PREF64,
PROP_IP6_NAMESERVERS, PROP_IP6_NAMESERVERS,
PROP_IP6_ADDRESSES, PROP_IP6_ADDRESSES,
PROP_IP6_ROUTES, ); PROP_IP6_ROUTES, );
@ -651,6 +681,12 @@ static const NMDBusInterfaceInfoExtended interface_info_ip6_config = {
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData", NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("AddressData",
"aa{sv}", "aa{sv}",
NM_IP_CONFIG_ADDRESS_DATA), NM_IP_CONFIG_ADDRESS_DATA),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ClatAddress",
"s",
NM_IP_CONFIG_CLAT_ADDRESS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ClatPref64",
"s",
NM_IP6_CONFIG_CLAT_PREF64),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP_CONFIG_GATEWAY), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Gateway", "s", NM_IP_CONFIG_GATEWAY),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE( NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(
"Routes", "Routes",
@ -682,11 +718,24 @@ get_property_ip6(GObject *object, guint prop_id, GValue *value, GParamSpec *pspe
guint len; guint len;
guint i; guint i;
const char *const *strarr; const char *const *strarr;
guint8 plen;
struct in6_addr addr6;
char sbuf[NM_UTILS_INET_ADDRSTRLEN];
switch (prop_id) { switch (prop_id) {
case PROP_IP6_ADDRESSES: case PROP_IP6_ADDRESSES:
g_value_set_variant(value, priv->v_addresses); g_value_set_variant(value, priv->v_addresses);
break; break;
case PROP_IP6_CLAT_PREF64:
if (nm_l3_config_data_get_clat_state(priv->l3cd, NULL, &addr6, &plen, NULL)) {
nm_inet6_ntop(&addr6, sbuf);
g_value_set_variant(value,
g_variant_new_string(
nm_sprintf_bufa(NM_INET_ADDRSTRLEN + 32, "%s/%u", sbuf, plen)));
} else {
g_value_set_variant(value, nm_g_variant_singleton_s_empty());
}
break;
case PROP_IP6_ROUTES: case PROP_IP6_ROUTES:
g_value_set_variant(value, priv->v_routes); g_value_set_variant(value, priv->v_routes);
break; break;
@ -740,6 +789,13 @@ nm_ip6_config_class_init(NMIP6ConfigClass *klass)
G_VARIANT_TYPE("a(ayuay)"), G_VARIANT_TYPE("a(ayuay)"),
NULL, NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties_ip6[PROP_IP6_CLAT_PREF64] =
g_param_spec_variant(NM_IP6_CONFIG_CLAT_PREF64,
"",
"",
G_VARIANT_TYPE("s"),
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties_ip6[PROP_IP6_ROUTES] = obj_properties_ip6[PROP_IP6_ROUTES] =
g_param_spec_variant(NM_IP6_CONFIG_ROUTES, g_param_spec_variant(NM_IP6_CONFIG_ROUTES,
"", "",
@ -787,7 +843,7 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd)
const int IS_IPv4 = NM_IS_IPv4(addr_family); const int IS_IPv4 = NM_IS_IPv4(addr_family);
NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self); NMIPConfigPrivate *priv = NM_IP_CONFIG_GET_PRIVATE(self);
nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL; nm_auto_unref_l3cd const NML3ConfigData *l3cd_old = NULL;
GParamSpec *changed_params[8]; GParamSpec *changed_params[10];
guint n_changed_params = 0; guint n_changed_params = 0;
const char *const *strarr; const char *const *strarr;
const char *const *strarr_old; const char *const *strarr_old;
@ -840,11 +896,64 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd)
} }
} }
/* CLAT state */
{
struct in6_addr clat_ip6;
struct in6_addr clat_pref64;
guint8 clat_pref64_plen;
in_addr_t clat_ip4;
gboolean clat_enabled;
struct in6_addr clat_ip6_old;
struct in6_addr clat_pref64_old;
guint8 clat_pref64_plen_old;
in_addr_t clat_ip4_old;
gboolean clat_enabled_old;
gboolean changed;
clat_enabled_old = nm_l3_config_data_get_clat_state(l3cd_old,
&clat_ip6_old,
&clat_pref64_old,
&clat_pref64_plen_old,
&clat_ip4_old);
clat_enabled = nm_l3_config_data_get_clat_state(priv->l3cd,
&clat_ip6,
&clat_pref64,
&clat_pref64_plen,
&clat_ip4);
/* CLAT address */
if (clat_enabled != clat_enabled_old) {
changed = TRUE;
} else if (!clat_enabled) {
changed = FALSE;
} else if (IS_IPv4) {
changed = (clat_ip4 != clat_ip4_old);
} else {
changed = !IN6_ARE_ADDR_EQUAL(&clat_ip6, &clat_ip6_old);
}
if (changed)
changed_params[n_changed_params++] = obj_properties_ip[PROP_IP_CLAT_ADDRESS];
/* PREF64 */
if (!IS_IPv4) {
if (clat_enabled != clat_enabled_old) {
changed = TRUE;
} else if (!clat_enabled) {
changed = FALSE;
} else {
changed = (clat_pref64_plen != clat_pref64_plen_old)
|| (!IN6_ARE_ADDR_EQUAL(&clat_pref64, &clat_pref64_old));
}
if (changed)
changed_params[n_changed_params++] = obj_properties_ip6[PROP_IP6_CLAT_PREF64];
}
}
_notify_all(self, changed_params, n_changed_params); _notify_all(self, changed_params, n_changed_params);
} }
static void static void
_handle_platform_change(NMIPConfig *self, guint32 obj_type_flags, gboolean is_init) _handle_platform_change(NMIPConfig *self, guint64 obj_type_flags, gboolean is_init)
{ {
const int addr_family = nm_ip_config_get_addr_family(self); const int addr_family = nm_ip_config_get_addr_family(self);
const int IS_IPv4 = NM_IS_IPv4(addr_family); const int IS_IPv4 = NM_IS_IPv4(addr_family);

View file

@ -35,7 +35,7 @@ struct _NMIPConfigPrivate {
GSource *notify_platform_timeout_source; GSource *notify_platform_timeout_source;
gint64 notify_platform_rlimited_until_msec; gint64 notify_platform_rlimited_until_msec;
gulong l3cfg_notify_id; gulong l3cfg_notify_id;
guint32 notify_platform_obj_type_flags; guint64 notify_platform_obj_type_flags;
}; };
struct _NMIPConfig { struct _NMIPConfig {

View file

@ -50,6 +50,9 @@ struct _NML3ConfigData {
const NMPObject *best_default_route_x[2]; const NMPObject *best_default_route_x[2];
}; };
struct in6_addr pref64_prefix;
guint32 pref64_plen;
GArray *wins; GArray *wins;
GArray *nis_servers; GArray *nis_servers;
@ -122,6 +125,19 @@ struct _NML3ConfigData {
NMSettingConnectionDnsOverTls dns_over_tls; NMSettingConnectionDnsOverTls dns_over_tls;
NMSettingConnectionDnssec dnssec; NMSettingConnectionDnssec dnssec;
NMUtilsIPv6IfaceId ip6_token; NMUtilsIPv6IfaceId ip6_token;
NMRefString *network_id;
NMSettingIp4ConfigClat clat_config; /* this indicates the 'administrative' CLAT
* state, i.e. whether CLAT will be started
* once we receive a PREF64 */
/* The runtime CLAT state */
struct {
struct in6_addr ip6;
struct in6_addr pref64;
in_addr_t ip4;
guint8 pref64_plen;
bool enabled;
} clat_state;
NML3ConfigDatFlags flags; NML3ConfigDatFlags flags;
@ -130,7 +146,8 @@ struct _NML3ConfigData {
int ndisc_hop_limit_val; int ndisc_hop_limit_val;
guint32 mtu; guint32 mtu;
guint32 ip6_mtu; guint32 ip6_mtu_static; /* IPv6 MTU from the connection profile */
guint32 ip6_mtu_ra; /* IPv6 MTU from Router Advertisement */
guint32 ndisc_reachable_time_msec_val; guint32 ndisc_reachable_time_msec_val;
guint32 ndisc_retrans_timer_msec_val; guint32 ndisc_retrans_timer_msec_val;
@ -167,6 +184,8 @@ struct _NML3ConfigData {
bool routed_dns_4 : 1; bool routed_dns_4 : 1;
bool routed_dns_6 : 1; bool routed_dns_6 : 1;
bool pref64_valid : 1;
}; };
/*****************************************************************************/ /*****************************************************************************/
@ -390,8 +409,11 @@ nm_l3_config_data_log(const NML3ConfigData *self,
: "", : "",
!self->is_sealed ? ", not-sealed" : ""); !self->is_sealed ? ", not-sealed" : "");
if (self->mtu != 0 || self->ip6_mtu != 0) { if (self->mtu != 0 || self->ip6_mtu_static != 0 || self->ip6_mtu_ra != 0) {
_L("mtu: %u, ip6-mtu: %u", self->mtu, self->ip6_mtu); _L("mtu: %u, ip6-mtu-static: %u, ip6-mtu-ra %u",
self->mtu,
self->ip6_mtu_static,
self->ip6_mtu_ra);
} }
for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
@ -519,6 +541,27 @@ nm_l3_config_data_log(const NML3ConfigData *self,
_L("nis-domain: %s", self->nis_domain->str); _L("nis-domain: %s", self->nis_domain->str);
} }
if (!IS_IPv4) {
if (self->clat_config == NM_SETTING_IP4_CONFIG_CLAT_AUTO)
_L("clat-config: auto");
else if (self->clat_config == NM_SETTING_IP4_CONFIG_CLAT_FORCE)
_L("clat-config: force");
if (self->clat_state.enabled) {
_L("clat-state: ip4=%s/32, pref64=%s/%u, ip6=%s/64",
nm_inet4_ntop(self->clat_state.ip4, sbuf + NM_INET_ADDRSTRLEN),
nm_inet6_ntop(&self->clat_state.pref64, sbuf),
self->clat_state.pref64_plen,
nm_inet6_ntop(&self->clat_state.ip6, sbuf_addr));
}
}
if (!IS_IPv4 && self->pref64_valid) {
_L("pref64_prefix: %s/%d",
nm_utils_inet6_ntop(&self->pref64_prefix, sbuf_addr),
self->pref64_plen);
}
if (self->dhcp_lease_x[IS_IPv4]) { if (self->dhcp_lease_x[IS_IPv4]) {
gs_free NMUtilsNamedValue *options_free = NULL; gs_free NMUtilsNamedValue *options_free = NULL;
NMUtilsNamedValue options_buffer[30]; NMUtilsNamedValue options_buffer[30];
@ -603,6 +646,10 @@ nm_l3_config_data_log(const NML3ConfigData *self,
nm_utils_inet6_interface_identifier_to_token(&self->ip6_token, sbuf_addr)); nm_utils_inet6_interface_identifier_to_token(&self->ip6_token, sbuf_addr));
} }
if (self->network_id) {
_L("network-id: %s", self->network_id->str);
}
if (self->metered != NM_TERNARY_DEFAULT) if (self->metered != NM_TERNARY_DEFAULT)
_L("metered: %s", self->metered ? "yes" : "no"); _L("metered: %s", self->metered ? "yes" : "no");
@ -709,6 +756,7 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc
.flags = NM_L3_CONFIG_DAT_FLAGS_NONE, .flags = NM_L3_CONFIG_DAT_FLAGS_NONE,
.metered = NM_TERNARY_DEFAULT, .metered = NM_TERNARY_DEFAULT,
.proxy_browser_only = NM_TERNARY_DEFAULT, .proxy_browser_only = NM_TERNARY_DEFAULT,
.clat_config = NM_SETTING_IP4_CONFIG_CLAT_NO,
.proxy_method = NM_PROXY_CONFIG_METHOD_UNKNOWN, .proxy_method = NM_PROXY_CONFIG_METHOD_UNKNOWN,
.route_table_sync_4 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE, .route_table_sync_4 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
.route_table_sync_6 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE, .route_table_sync_6 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE,
@ -722,6 +770,7 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc
.ndisc_retrans_timer_msec_set = FALSE, .ndisc_retrans_timer_msec_set = FALSE,
.allow_routes_without_address_4 = TRUE, .allow_routes_without_address_4 = TRUE,
.allow_routes_without_address_6 = TRUE, .allow_routes_without_address_6 = TRUE,
.pref64_valid = FALSE,
}; };
_idx_type_init(&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS); _idx_type_init(&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS);
@ -822,6 +871,7 @@ nm_l3_config_data_unref(const NML3ConfigData *self)
nm_ref_string_unref(mutable->nis_domain); nm_ref_string_unref(mutable->nis_domain);
nm_ref_string_unref(mutable->proxy_pac_url); nm_ref_string_unref(mutable->proxy_pac_url);
nm_ref_string_unref(mutable->proxy_pac_script); nm_ref_string_unref(mutable->proxy_pac_script);
nm_ref_string_unref(mutable->network_id);
nm_g_slice_free(mutable); nm_g_slice_free(mutable);
} }
@ -1890,22 +1940,42 @@ nm_l3_config_data_set_mtu(NML3ConfigData *self, guint32 mtu)
} }
guint32 guint32
nm_l3_config_data_get_ip6_mtu(const NML3ConfigData *self) nm_l3_config_data_get_ip6_mtu_static(const NML3ConfigData *self)
{ {
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE)); nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
return self->ip6_mtu; return self->ip6_mtu_static;
} }
gboolean gboolean
nm_l3_config_data_set_ip6_mtu(NML3ConfigData *self, guint32 ip6_mtu) nm_l3_config_data_set_ip6_mtu_static(NML3ConfigData *self, guint32 ip6_mtu)
{ {
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE)); nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
if (self->ip6_mtu == ip6_mtu) if (self->ip6_mtu_static == ip6_mtu)
return FALSE; return FALSE;
self->ip6_mtu = ip6_mtu; self->ip6_mtu_static = ip6_mtu;
return TRUE;
}
guint32
nm_l3_config_data_get_ip6_mtu_ra(const NML3ConfigData *self)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
return self->ip6_mtu_ra;
}
gboolean
nm_l3_config_data_set_ip6_mtu_ra(NML3ConfigData *self, guint32 ip6_mtu)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
if (self->ip6_mtu_ra == ip6_mtu)
return FALSE;
self->ip6_mtu_ra = ip6_mtu;
return TRUE; return TRUE;
} }
@ -1957,6 +2027,132 @@ nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_to
return TRUE; return TRUE;
} }
const char *
nm_l3_config_data_get_network_id(const NML3ConfigData *self)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
return nm_ref_string_get_str(self->network_id);
}
gboolean
nm_l3_config_data_set_network_id(NML3ConfigData *self, const char *value)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
return nm_ref_string_reset_str(&self->network_id, value);
}
gboolean
nm_l3_config_data_set_clat_config(NML3ConfigData *self, NMSettingIp4ConfigClat val)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
nm_assert(NM_IN_SET(val,
NM_SETTING_IP4_CONFIG_CLAT_NO,
NM_SETTING_IP4_CONFIG_CLAT_FORCE,
NM_SETTING_IP4_CONFIG_CLAT_AUTO));
if (self->clat_config == val)
return FALSE;
self->clat_config = val;
return TRUE;
}
NMSettingIp4ConfigClat
nm_l3_config_data_get_clat_config(const NML3ConfigData *self)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
return self->clat_config;
}
gboolean
nm_l3_config_data_get_clat_state(const NML3ConfigData *self,
struct in6_addr *out_ip6,
struct in6_addr *out_pref64,
guint8 *out_pref64_plen,
in_addr_t *out_ip4)
{
if (!self || !self->clat_state.enabled)
return FALSE;
NM_SET_OUT(out_ip6, self->clat_state.ip6);
NM_SET_OUT(out_pref64, self->clat_state.pref64);
NM_SET_OUT(out_pref64_plen, self->clat_state.pref64_plen);
NM_SET_OUT(out_ip4, self->clat_state.ip4);
return TRUE;
}
void
nm_l3_config_data_set_clat_state(NML3ConfigData *self,
gboolean enabled,
const struct in6_addr *ip6,
const struct in6_addr *pref64,
guint8 pref64_plen,
in_addr_t ip4)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
self->clat_state.enabled = enabled;
if (enabled) {
self->clat_state.ip6 = *ip6;
self->clat_state.pref64 = *pref64;
self->clat_state.pref64_plen = pref64_plen;
self->clat_state.ip4 = ip4;
} else {
self->clat_state.ip6 = in6addr_any;
self->clat_state.pref64 = in6addr_any;
self->clat_state.pref64_plen = 0;
self->clat_state.ip4 = 0;
}
}
gboolean
nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val)
{
if (self->pref64_valid == val)
return FALSE;
self->pref64_valid = val;
return TRUE;
}
gboolean
nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
return self->pref64_valid;
}
gboolean
nm_l3_config_data_get_pref64(const NML3ConfigData *self,
struct in6_addr *out_prefix,
guint32 *out_plen)
{
nm_assert(_NM_IS_L3_CONFIG_DATA(self, TRUE));
if (!self->pref64_valid)
return FALSE;
NM_SET_OUT(out_prefix, self->pref64_prefix);
NM_SET_OUT(out_plen, self->pref64_plen);
return TRUE;
}
gboolean
nm_l3_config_data_set_pref64(NML3ConfigData *self, struct in6_addr prefix, guint32 plen)
{
if (self->pref64_valid) {
if (self->pref64_plen == plen
&& nm_ip6_addr_same_prefix(&self->pref64_prefix, &prefix, plen)) {
return FALSE;
}
} else {
self->pref64_valid = TRUE;
}
self->pref64_prefix = prefix;
self->pref64_plen = plen;
return TRUE;
}
NMMptcpFlags NMMptcpFlags
nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self) nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self)
{ {
@ -2484,8 +2680,10 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a,
if (NM_FLAGS_HAS(flags, NM_L3_CONFIG_CMP_FLAGS_OTHER)) { if (NM_FLAGS_HAS(flags, NM_L3_CONFIG_CMP_FLAGS_OTHER)) {
NM_CMP_DIRECT(a->flags, b->flags); NM_CMP_DIRECT(a->flags, b->flags);
NM_CMP_DIRECT(a->ip6_token.id, b->ip6_token.id); NM_CMP_DIRECT(a->ip6_token.id, b->ip6_token.id);
NM_CMP_DIRECT_REF_STRING(a->network_id, b->network_id);
NM_CMP_DIRECT(a->mtu, b->mtu); NM_CMP_DIRECT(a->mtu, b->mtu);
NM_CMP_DIRECT(a->ip6_mtu, b->ip6_mtu); NM_CMP_DIRECT(a->ip6_mtu_static, b->ip6_mtu_static);
NM_CMP_DIRECT(a->ip6_mtu_ra, b->ip6_mtu_ra);
NM_CMP_DIRECT_UNSAFE(a->metered, b->metered); NM_CMP_DIRECT_UNSAFE(a->metered, b->metered);
NM_CMP_DIRECT_UNSAFE(a->proxy_browser_only, b->proxy_browser_only); NM_CMP_DIRECT_UNSAFE(a->proxy_browser_only, b->proxy_browser_only);
NM_CMP_DIRECT_UNSAFE(a->proxy_method, b->proxy_method); NM_CMP_DIRECT_UNSAFE(a->proxy_method, b->proxy_method);
@ -2509,6 +2707,23 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a,
NM_CMP_DIRECT_UNSAFE(a->routed_dns_4, b->routed_dns_4); NM_CMP_DIRECT_UNSAFE(a->routed_dns_4, b->routed_dns_4);
NM_CMP_DIRECT_UNSAFE(a->routed_dns_6, b->routed_dns_6); NM_CMP_DIRECT_UNSAFE(a->routed_dns_6, b->routed_dns_6);
NM_CMP_DIRECT_UNSAFE(a->clat_config, b->clat_config);
NM_CMP_DIRECT(!!a->clat_state.enabled, !!b->clat_state.enabled);
if (a->clat_state.enabled) {
NM_CMP_DIRECT_IN6ADDR(&a->clat_state.ip6, &b->clat_state.ip6);
NM_CMP_DIRECT_IN6ADDR(&a->clat_state.pref64, &b->clat_state.pref64);
NM_CMP_DIRECT(a->clat_state.pref64_plen, b->clat_state.pref64_plen);
NM_CMP_DIRECT(a->clat_state.ip4, b->clat_state.ip4);
}
NM_CMP_DIRECT(!!a->pref64_valid, !!b->pref64_valid);
if (a->pref64_valid) {
NM_CMP_DIRECT(a->pref64_plen, b->pref64_plen);
NM_CMP_RETURN_DIRECT(
nm_ip6_addr_same_prefix_cmp(&a->pref64_prefix, &b->pref64_prefix, a->pref64_plen));
}
NM_CMP_FIELD(a, b, source); NM_CMP_FIELD(a, b, source);
} }
@ -2524,8 +2739,10 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a,
/*****************************************************************************/ /*****************************************************************************/
static const NMPObject * const NMPObject *
_data_get_direct_route_for_host(const NML3ConfigData *self, int addr_family, gconstpointer host) nm_l3_config_data_get_direct_route_for_host(const NML3ConfigData *self,
int addr_family,
gconstpointer host)
{ {
const int IS_IPv4 = NM_IS_IPv4(addr_family); const int IS_IPv4 = NM_IS_IPv4(addr_family);
const NMPObject *best_route_obj = NULL; const NMPObject *best_route_obj = NULL;
@ -2683,7 +2900,7 @@ nm_l3_config_data_add_dependent_onlink_routes(NML3ConfigData *self, int addr_fam
if (NM_FLAGS_HAS(route_src->rx.r_rtm_flags, (unsigned) RTNH_F_ONLINK)) if (NM_FLAGS_HAS(route_src->rx.r_rtm_flags, (unsigned) RTNH_F_ONLINK))
continue; continue;
if (_data_get_direct_route_for_host(self, addr_family, p_gateway)) if (nm_l3_config_data_get_direct_route_for_host(self, addr_family, p_gateway))
continue; continue;
new_route = nmp_object_clone(obj_src, FALSE); new_route = nmp_object_clone(obj_src, FALSE);
@ -2991,12 +3208,9 @@ _init_from_connection_ip(NML3ConfigData *self, int addr_family, NMConnection *co
* the one we create here (because the "onlink" flag is part of the * the one we create here (because the "onlink" flag is part of the
* identifier of a route, see nm_platform_ip4_route_cmp()). * identifier of a route, see nm_platform_ip4_route_cmp()).
* *
* Note however that for ECMP routes we currently can only merge routes * The onlink flag is tracked per-nexthop (in NMPlatformIP4RtNextHop.rtnh_flags
* that agree in their onlink flag. So a route without gateway cannot * for extra nexthops, and in r_rtm_flags for the first nexthop). ECMP routes
* merge with an onlink route that has a gateway. That needs fixing, * can be merged regardless of per-nexthop onlink flags. */
* by not treating the onlink flag as for the entire route, but allowing
* to merge ECMP routes with different onlink flag. And first, we need
* to track the onlink flag for the nexthop (NMPlatformIP4RtNextHop). */
r.r4.r_rtm_flags &= ~((unsigned) RTNH_F_ONLINK); r.r4.r_rtm_flags &= ~((unsigned) RTNH_F_ONLINK);
} }
@ -3030,6 +3244,9 @@ _init_from_connection_ip(NML3ConfigData *self, int addr_family, NMConnection *co
nm_l3_config_data_set_ip6_privacy( nm_l3_config_data_set_ip6_privacy(
self, self,
nm_setting_ip6_config_get_ip6_privacy(NM_SETTING_IP6_CONFIG(s_ip))); nm_setting_ip6_config_get_ip6_privacy(NM_SETTING_IP6_CONFIG(s_ip)));
nm_l3_config_data_set_ip6_mtu_static(
self,
nm_setting_ip6_config_get_mtu(NM_SETTING_IP6_CONFIG(s_ip)));
} }
} }
@ -3506,6 +3723,9 @@ nm_l3_config_data_merge(NML3ConfigData *self,
if (self->ip6_token.id == 0) if (self->ip6_token.id == 0)
self->ip6_token.id = src->ip6_token.id; self->ip6_token.id = src->ip6_token.id;
if (!self->network_id)
self->network_id = nm_ref_string_ref(src->network_id);
self->metered = NM_MAX((NMTernary) self->metered, (NMTernary) src->metered); self->metered = NM_MAX((NMTernary) self->metered, (NMTernary) src->metered);
if (self->proxy_method == NM_PROXY_CONFIG_METHOD_UNKNOWN) if (self->proxy_method == NM_PROXY_CONFIG_METHOD_UNKNOWN)
@ -3544,8 +3764,11 @@ nm_l3_config_data_merge(NML3ConfigData *self,
if (self->mtu == 0u) if (self->mtu == 0u)
self->mtu = src->mtu; self->mtu = src->mtu;
if (self->ip6_mtu == 0u) if (self->ip6_mtu_static == 0u)
self->ip6_mtu = src->ip6_mtu; self->ip6_mtu_static = src->ip6_mtu_static;
if (self->ip6_mtu_ra == 0u)
self->ip6_mtu_ra = src->ip6_mtu_ra;
if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) { if (NM_FLAGS_HAS(merge_flags, NM_L3_CONFIG_MERGE_FLAGS_CLONE)) {
_nm_unused nm_auto_unref_dhcplease NMDhcpLease *dhcp_lease_6 = _nm_unused nm_auto_unref_dhcplease NMDhcpLease *dhcp_lease_6 =
@ -3567,6 +3790,24 @@ nm_l3_config_data_merge(NML3ConfigData *self,
self->routed_dns_4 = TRUE; self->routed_dns_4 = TRUE;
if (src->routed_dns_6) if (src->routed_dns_6)
self->routed_dns_6 = TRUE; self->routed_dns_6 = TRUE;
if (self->clat_config == NM_SETTING_IP4_CONFIG_CLAT_NO) {
/* 'no' always loses to 'force' and 'auto' */
self->clat_config = src->clat_config;
} else if (src->clat_config == NM_SETTING_IP4_CONFIG_CLAT_FORCE) {
/* 'force' always takes precedence */
self->clat_config = src->clat_config;
}
if (!self->clat_state.enabled && src->clat_state.enabled) {
self->clat_state = src->clat_state;
}
if (src->pref64_valid) {
self->pref64_prefix = src->pref64_prefix;
self->pref64_plen = src->pref64_plen;
self->pref64_valid = src->pref64_valid;
}
} }
NML3ConfigData * NML3ConfigData *

View file

@ -5,6 +5,7 @@
#include "libnm-glib-aux/nm-dedup-multi.h" #include "libnm-glib-aux/nm-dedup-multi.h"
#include "nm-setting-connection.h" #include "nm-setting-connection.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h" #include "nm-setting-ip6-config.h"
#include "libnm-platform/nm-platform.h" #include "libnm-platform/nm-platform.h"
#include "libnm-platform/nmp-object.h" #include "libnm-platform/nmp-object.h"
@ -225,6 +226,10 @@ nm_l3_config_data_equal(const NML3ConfigData *a, const NML3ConfigData *b)
/*****************************************************************************/ /*****************************************************************************/
const NMPObject *nm_l3_config_data_get_direct_route_for_host(const NML3ConfigData *self,
int addr_family,
gconstpointer host);
const NMDedupMultiIdxType *nm_l3_config_data_lookup_index(const NML3ConfigData *self, const NMDedupMultiIdxType *nm_l3_config_data_lookup_index(const NML3ConfigData *self,
NMPObjectType obj_type); NMPObjectType obj_type);
@ -482,14 +487,49 @@ guint32 nm_l3_config_data_get_mtu(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_mtu(NML3ConfigData *self, guint32 mtu); gboolean nm_l3_config_data_set_mtu(NML3ConfigData *self, guint32 mtu);
guint32 nm_l3_config_data_get_ip6_mtu(const NML3ConfigData *self); guint32 nm_l3_config_data_get_ip6_mtu_static(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_ip6_mtu(NML3ConfigData *self, guint32 ip6_mtu); gboolean nm_l3_config_data_set_ip6_mtu_static(NML3ConfigData *self, guint32 ip6_mtu);
guint32 nm_l3_config_data_get_ip6_mtu_ra(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_ip6_mtu_ra(NML3ConfigData *self, guint32 ip6_mtu);
NMUtilsIPv6IfaceId nm_l3_config_data_get_ip6_token(const NML3ConfigData *self); NMUtilsIPv6IfaceId nm_l3_config_data_get_ip6_token(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_token); gboolean nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_token);
gboolean nm_l3_config_data_set_network_id(NML3ConfigData *self, const char *network_id);
const char *nm_l3_config_data_get_network_id(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_clat_config(NML3ConfigData *self, NMSettingIp4ConfigClat val);
NMSettingIp4ConfigClat nm_l3_config_data_get_clat_config(const NML3ConfigData *self);
gboolean nm_l3_config_data_get_clat_state(const NML3ConfigData *self,
struct in6_addr *out_ip6,
struct in6_addr *out_pref64,
guint8 *out_pref64_plen,
in_addr_t *out_ip4);
void nm_l3_config_data_set_clat_state(NML3ConfigData *self,
gboolean enabled,
const struct in6_addr *ip6,
const struct in6_addr *pref64,
guint8 pref64_plen,
in_addr_t ip4);
gboolean nm_l3_config_data_set_pref64_valid(NML3ConfigData *self, gboolean val);
gboolean nm_l3_config_data_get_pref64_valid(const NML3ConfigData *self);
gboolean nm_l3_config_data_get_pref64(const NML3ConfigData *self,
struct in6_addr *out_prefix,
guint32 *out_plen);
gboolean nm_l3_config_data_set_pref64(NML3ConfigData *self, struct in6_addr prefix, guint32 plen);
NMMptcpFlags nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self); NMMptcpFlags nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self);
gboolean nm_l3_config_data_set_mptcp_flags(NML3ConfigData *self, NMMptcpFlags mptcp_flags); gboolean nm_l3_config_data_set_mptcp_flags(NML3ConfigData *self, NMMptcpFlags mptcp_flags);

View file

@ -7,10 +7,15 @@
#include "libnm-std-aux/nm-linux-compat.h" #include "libnm-std-aux/nm-linux-compat.h"
#include <net/if.h> #include <net/if.h>
#include <net/if_arp.h>
#include "nm-compat-headers/linux/if_addr.h" #include "nm-compat-headers/linux/if_addr.h"
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/fib_rules.h> #include <linux/fib_rules.h>
#if HAVE_CLAT
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#endif /* HAVE_CLAT */
#include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "libnm-glib-aux/nm-prioq.h" #include "libnm-glib-aux/nm-prioq.h"
@ -22,6 +27,13 @@
#include "n-acd/src/n-acd.h" #include "n-acd/src/n-acd.h"
#include "nm-l3-ipv4ll.h" #include "nm-l3-ipv4ll.h"
#include "nm-ip-config.h" #include "nm-ip-config.h"
#include "nm-core-utils.h"
#if HAVE_CLAT
#include "bpf/clat.h"
NM_PRAGMA_WARNING_DISABLE("-Wcast-align")
#include "bpf/clat.skel.h"
NM_PRAGMA_WARNING_REENABLE
#endif /* HAVE_CLAT */
/*****************************************************************************/ /*****************************************************************************/
@ -289,6 +301,24 @@ typedef struct _NML3CfgPrivate {
NMIPConfig *ipconfig_x[2]; NMIPConfig *ipconfig_x[2];
}; };
#if HAVE_CLAT
/* The reserved IPv4 address for CLAT in the 192.0.0.0/28 range */
NMNetnsIPReservation *clat_address_4;
/* The IPv6 address for sending and receiving translated packets */
NMPlatformIP6Address clat_address_6;
/* The same addresses as above, but already committed previously */
NMNetnsIPReservation *clat_address_4_committed;
NMPlatformIP6Address clat_address_6_committed;
/* If NULL, the BPF program hasn't been loaded or attached */
struct clat_bpf *clat_bpf;
struct bpf_link *clat_ingress_link;
struct bpf_link *clat_egress_link;
int clat_socket;
#endif /* HAVE_CLAT */
/* Whether we earlier configured MPTCP endpoints for the interface. */ /* Whether we earlier configured MPTCP endpoints for the interface. */
union { union {
struct { struct {
@ -353,6 +383,9 @@ typedef struct _NML3CfgPrivate {
bool rp_filter_handled : 1; bool rp_filter_handled : 1;
bool rp_filter_set : 1; bool rp_filter_set : 1;
bool clat_address_6_valid : 1;
bool clat_address_6_committed_valid : 1;
} NML3CfgPrivate; } NML3CfgPrivate;
struct _NML3CfgClass { struct _NML3CfgClass {
@ -624,7 +657,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE:
nm_strbuf_append(&s, nm_strbuf_append(&s,
&l, &l,
", obj-type-flags=0x%x", ", obj-type-flags=0x%" G_GINT64_MODIFIER "x",
notify_data->platform_change_on_idle.obj_type_flags); notify_data->platform_change_on_idle.obj_type_flags);
break; break;
case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT: case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT:
@ -1571,7 +1604,7 @@ _load_link(NML3Cfg *self, gboolean initial)
/*****************************************************************************/ /*****************************************************************************/
void void
_nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags) _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint64 obj_type_flags)
{ {
NML3ConfigNotifyData notify_data; NML3ConfigNotifyData notify_data;
@ -4107,6 +4140,233 @@ update_routes:
} }
} }
static void
_l3cfg_update_clat_config(NML3Cfg *self,
NML3ConfigData *l3cd,
const L3ConfigData **l3_config_datas_arr,
guint l3_config_datas_len)
{
#if !HAVE_CLAT
return;
#else
struct in6_addr pref64;
guint32 pref64_plen;
gboolean clat_enabled = FALSE;
const NMPlatformIP4Route *ip4_route;
NMDedupMultiIter iter;
switch (nm_l3_config_data_get_clat_config(l3cd)) {
case NM_SETTING_IP4_CONFIG_CLAT_FORCE:
clat_enabled = TRUE;
break;
case NM_SETTING_IP4_CONFIG_CLAT_NO:
clat_enabled = FALSE;
break;
case NM_SETTING_IP4_CONFIG_CLAT_AUTO:
clat_enabled = TRUE;
/* disable if there is a native IPv4 gateway */
nm_l3_config_data_iter_ip4_route_for_each (&iter, l3cd, &ip4_route) {
if (ip4_route->network == INADDR_ANY && ip4_route->plen == 0
&& ip4_route->gateway != INADDR_ANY)
clat_enabled = FALSE;
break;
}
break;
case NM_SETTING_IP4_CONFIG_CLAT_DEFAULT:
nm_assert_not_reached();
clat_enabled = TRUE;
break;
}
if (clat_enabled && nm_l3_config_data_get_pref64_valid(l3cd)) {
NMPlatformIPXRoute rx;
NMIPAddrTyped best_v6_gateway;
const NMPlatformIP6Route *best_v6_route;
const NMPlatformIP6Address *ip6_entry;
struct in6_addr ip6;
const char *network_id;
char buf[512];
guint32 route4_metric = NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4;
guint i;
/* If we have a valid NAT64 prefix, configure in kernel:
*
* - a CLAT IPv4 address (192.0.0.x)
* - a IPv4 default route via the best IPv6 gateway
*
* We also set clat_address_6 as an additional /64 IPv6 address
* determined according to https://www.rfc-editor.org/rfc/rfc6877#section-6.3 .
* This address is used for sending and receiving translated packets,
* but is not configured in kernel to avoid that it gets used by applications.
* Later in _l3_commit_pref64() we use IPV6_JOIN_ANYCAST to let the kernel
* handle ND for the address.
*/
nm_l3_config_data_get_pref64(l3cd, &pref64, &pref64_plen);
network_id = nm_l3_config_data_get_network_id(l3cd);
if (!self->priv.p->clat_address_6_valid && network_id) {
nm_l3_config_data_iter_ip6_address_for_each (&iter, l3cd, &ip6_entry) {
if (ip6_entry->addr_source == NM_IP_CONFIG_SOURCE_NDISC && ip6_entry->plen == 64) {
ip6 = ip6_entry->address;
nm_utils_ipv6_addr_set_stable_privacy(NM_UTILS_STABLE_TYPE_CLAT,
&ip6,
nm_l3cfg_get_ifname(self, TRUE),
network_id,
0);
self->priv.p->clat_address_6 = (NMPlatformIP6Address) {
.ifindex = self->priv.ifindex,
.address = ip6,
.peer_address = ip6,
.addr_source = NM_IP_CONFIG_SOURCE_CLAT,
.plen = ip6_entry->plen,
};
_LOGT("clat: using IPv6 address %s", nm_inet6_ntop(&ip6, buf));
self->priv.p->clat_address_6_valid = TRUE;
break;
}
}
}
/* Don't get a v4 address if we have no v6 address (otherwise, we could
* potentially create broken v4 connectivity) */
if (!self->priv.p->clat_address_6_valid) {
_LOGW("CLAT is currently only supported when SLAAC is in use.");
/* Deallocate the v4 address unless it's the committed one */
if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_committed) {
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
} else {
self->priv.p->clat_address_4 = NULL;
}
} else if (!self->priv.p->clat_address_4) {
/* We need a v4 /32 */
self->priv.p->clat_address_4 =
nm_netns_ip_reservation_get(self->priv.netns, NM_NETNS_IP_RESERVATION_TYPE_CLAT);
}
{
const NMPlatformIP4Route *r4;
guint32 metric = 0;
guint32 penalty = 0;
/* Find the IPv4 metric for the CLAT default route.
* If there is another non-CLAT default route on the device, use the
* same metric + 1, so that native connectivity is always preferred.
* Otherwise, use the metric from the connection profile.
*/
r4 = NMP_OBJECT_CAST_IP4_ROUTE(nm_l3_config_data_get_best_default_route(l3cd, AF_INET));
if (r4) {
route4_metric = nm_add_clamped_u32(r4->metric, 1u);
} else {
for (i = 0; i < l3_config_datas_len; i++) {
const L3ConfigData *l3cd_data = l3_config_datas_arr[i];
if (l3cd_data->default_route_metric_4 != NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4) {
metric = l3cd_data->default_route_metric_4;
}
if (l3cd_data->default_route_penalty_4 != 0) {
penalty = l3cd_data->default_route_penalty_4;
}
}
route4_metric = nm_add_clamped_u32(metric, penalty);
}
}
if (self->priv.p->clat_address_4) {
best_v6_route = NMP_OBJECT_CAST_IP6_ROUTE(
nm_l3_config_data_get_direct_route_for_host(l3cd, AF_INET6, &pref64));
if (!best_v6_route) {
best_v6_route = NMP_OBJECT_CAST_IP6_ROUTE(
nm_l3_config_data_get_best_default_route(l3cd, AF_INET6));
}
if (best_v6_route) {
NMPlatformIP4Address addr = {
.ifindex = self->priv.ifindex,
.address = self->priv.p->clat_address_4->addr,
.peer_address = self->priv.p->clat_address_4->addr,
.addr_source = NM_IP_CONFIG_SOURCE_CLAT,
.plen = 32,
};
const NMPlatformLink *pllink;
guint mtu = 0;
guint val = 0;
best_v6_gateway.addr_family = AF_INET6;
best_v6_gateway.addr.addr6 = best_v6_route->gateway;
/* Determine the IPv6 MTU of the interface. Unfortunately,
* the logic to set the MTU is in NMDevice and here we need
* some duplication to find the actual value.
* TODO: move the MTU handling into l3cfg. */
/* Get the link MTU */
pllink = nm_l3cfg_get_pllink(self, TRUE);
if (pllink)
mtu = pllink->mtu;
if (mtu == 0)
mtu = 1500;
/* Update it with the IPv6 MTU value from the connection
* or from RA */
val = nm_l3_config_data_get_ip6_mtu_static(l3cd);
if (val == 0) {
val = nm_l3_config_data_get_ip6_mtu_ra(l3cd);
}
if (val != 0 && val < mtu) {
mtu = val;
}
if (mtu < 1280)
mtu = 1280;
/* Leave 20 additional bytes for the ipv4 -> ipv6 header translation,
* plus 8 for a potential fragmentation extension header */
mtu -= 28;
rx.r4 = (NMPlatformIP4Route) {
.ifindex = self->priv.ifindex,
.rt_source = NM_IP_CONFIG_SOURCE_CLAT,
.network = 0, /* default route */
.plen = 0,
.table_coerced = nm_platform_route_table_coerce(RT_TABLE_MAIN),
.scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE),
.type_coerced = nm_platform_route_type_coerce(RTN_UNICAST),
.pref_src = self->priv.p->clat_address_4->addr,
.via = best_v6_gateway,
.metric = route4_metric,
.mtu = mtu,
};
nm_platform_ip_route_normalize(AF_INET, &rx.rx);
if (!nm_l3_config_data_lookup_route(l3cd, AF_INET, &rx.rx)) {
nm_l3_config_data_add_route_4(l3cd, &rx.r4);
}
_LOGT("clat: route %s", nm_platform_ip4_route_to_string(&rx.r4, buf, sizeof(buf)));
nm_l3_config_data_add_address_4(l3cd, &addr);
} else {
_LOGW("Couldn't find a good ipv6 route! Unable to set up CLAT!");
}
}
if (self->priv.p->clat_address_4 && self->priv.p->clat_address_6_valid) {
nm_l3_config_data_set_clat_state(l3cd,
TRUE,
&self->priv.p->clat_address_6.address,
&pref64,
pref64_plen,
self->priv.p->clat_address_4->addr);
} else {
nm_l3_config_data_set_clat_state(l3cd, FALSE, NULL, NULL, 0, INADDR_ANY);
}
}
#endif /* HAVE_CLAT */
}
static void static void
_l3cfg_update_combined_config(NML3Cfg *self, _l3cfg_update_combined_config(NML3Cfg *self,
gboolean to_commit, gboolean to_commit,
@ -4220,6 +4480,8 @@ _l3cfg_update_combined_config(NML3Cfg *self,
&hook_data); &hook_data);
} }
_l3cfg_update_clat_config(self, l3cd, l3_config_datas_arr, l3_config_datas_len);
if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) { if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) {
NMPlatformIPXAddress ax; NMPlatformIPXAddress ax;
NMPlatformIPXRoute rx; NMPlatformIPXRoute rx;
@ -4280,6 +4542,18 @@ _l3cfg_update_combined_config(NML3Cfg *self,
if (nm_l3_config_data_equal(l3cd, self->priv.p->combined_l3cd_merged)) if (nm_l3_config_data_equal(l3cd, self->priv.p->combined_l3cd_merged))
goto out; goto out;
#if HAVE_CLAT
if (!l3cd) {
self->priv.p->clat_address_6_valid = FALSE;
/* Deallocate the v4 address unless it's the commited one */
if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_committed) {
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
} else {
self->priv.p->clat_address_4 = NULL;
}
}
#endif /* HAVE_CLAT */
l3cd_old = g_steal_pointer(&self->priv.p->combined_l3cd_merged); l3cd_old = g_steal_pointer(&self->priv.p->combined_l3cd_merged);
self->priv.p->combined_l3cd_merged = nm_l3_config_data_seal(g_steal_pointer(&l3cd)); self->priv.p->combined_l3cd_merged = nm_l3_config_data_seal(g_steal_pointer(&l3cd));
merged_changed = TRUE; merged_changed = TRUE;
@ -5379,6 +5653,251 @@ _l3_commit_one(NML3Cfg *self,
_failedobj_handle_routes(self, addr_family, routes_failed); _failedobj_handle_routes(self, addr_family, routes_failed);
} }
#if HAVE_CLAT
static void
_l3_clat_destroy(NML3Cfg *self)
{
char buf[100];
int err;
if (self->priv.p->clat_bpf) {
const struct clat_stats *s = &self->priv.p->clat_bpf->bss->stats;
_LOGT("clat: stats:"
" egress (v4 to v6): tcp %" G_GUINT64_FORMAT ", udp %" G_GUINT64_FORMAT
", icmp %" G_GUINT64_FORMAT ", other %" G_GUINT64_FORMAT
", dropped %" G_GUINT64_FORMAT "; ingress (v6 to v4): tcp %" G_GUINT64_FORMAT
", udp %" G_GUINT64_FORMAT ", icmp %" G_GUINT64_FORMAT ", other %" G_GUINT64_FORMAT
", fragment %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT,
(guint64) s->egress_tcp,
(guint64) s->egress_udp,
(guint64) s->egress_icmp,
(guint64) s->egress_other,
(guint64) s->egress_dropped,
(guint64) s->ingress_tcp,
(guint64) s->ingress_udp,
(guint64) s->ingress_icmp,
(guint64) s->ingress_other,
(guint64) s->ingress_fragment,
(guint64) s->ingress_dropped);
}
if (self->priv.p->clat_ingress_link) {
err = bpf_link__destroy(self->priv.p->clat_ingress_link);
if (err != 0) {
libbpf_strerror(err, buf, sizeof(buf));
_LOGD("clat: failed to destroy the ingress link");
}
self->priv.p->clat_ingress_link = NULL;
}
if (self->priv.p->clat_egress_link) {
err = bpf_link__destroy(self->priv.p->clat_egress_link);
if (err != 0) {
libbpf_strerror(err, buf, sizeof(buf));
_LOGD("clat: failed to destroy the egress link");
}
self->priv.p->clat_egress_link = NULL;
}
nm_clear_pointer(&self->priv.p->clat_bpf, clat_bpf__destroy);
}
static void
_l3_commit_pref64(NML3Cfg *self, NML3CfgCommitType commit_type)
{
int err = 0;
const NML3ConfigData *l3cd = self->priv.p->combined_l3cd_commited;
struct in6_addr _l3cd_pref64_inner;
const struct in6_addr *l3cd_pref64 = NULL;
guint32 l3cd_pref64_plen;
char buf[100];
struct clat_config clat_config;
gboolean v6_changed;
const NMPlatformLink *pllink;
gboolean has_ethernet_header = FALSE;
if (l3cd && nm_l3_config_data_get_pref64(l3cd, &_l3cd_pref64_inner, &l3cd_pref64_plen)) {
l3cd_pref64 = &_l3cd_pref64_inner;
}
if (l3cd_pref64 && self->priv.p->clat_address_4 && self->priv.p->clat_address_6_valid) {
pllink = nm_l3cfg_get_pllink(self, TRUE);
if (!pllink) {
has_ethernet_header = TRUE;
} else {
switch (pllink->arptype) {
case ARPHRD_ETHER:
has_ethernet_header = TRUE;
break;
case ARPHRD_NONE:
case ARPHRD_PPP:
case ARPHRD_RAWIP:
has_ethernet_header = FALSE;
break;
default:
_LOGD("clat: unknown ARP type %u, assuming the interface uses no L2 header",
pllink->arptype);
has_ethernet_header = FALSE;
}
}
if (!self->priv.p->clat_bpf) {
_LOGT("clat: attaching the BPF program");
self->priv.p->clat_bpf = clat_bpf__open();
if (!self->priv.p->clat_bpf) {
libbpf_strerror(errno, buf, sizeof(buf));
_LOGW("clat: failed to open the BPF program: %s", buf);
return;
}
/* Only load the programs for the right L2 type */
bpf_program__set_autoload(self->priv.p->clat_bpf->progs.nm_clat_ingress_eth,
has_ethernet_header);
bpf_program__set_autoload(self->priv.p->clat_bpf->progs.nm_clat_egress_eth,
has_ethernet_header);
bpf_program__set_autoload(self->priv.p->clat_bpf->progs.nm_clat_ingress_rawip,
!has_ethernet_header);
bpf_program__set_autoload(self->priv.p->clat_bpf->progs.nm_clat_egress_rawip,
!has_ethernet_header);
if (clat_bpf__load(self->priv.p->clat_bpf)) {
libbpf_strerror(errno, buf, sizeof(buf));
_LOGW("clat: failed to load the BPF program: %s", buf);
nm_clear_pointer(&self->priv.p->clat_bpf, clat_bpf__destroy);
return;
}
self->priv.p->clat_ingress_link = bpf_program__attach_tcx(
has_ethernet_header ? self->priv.p->clat_bpf->progs.nm_clat_ingress_eth
: self->priv.p->clat_bpf->progs.nm_clat_ingress_rawip,
self->priv.ifindex,
NULL);
if (!self->priv.p->clat_ingress_link) {
libbpf_strerror(errno, buf, sizeof(buf));
_LOGW("clat: failed to attach the ingress program: %s", buf);
return;
}
self->priv.p->clat_egress_link = bpf_program__attach_tcx(
has_ethernet_header ? self->priv.p->clat_bpf->progs.nm_clat_egress_eth
: self->priv.p->clat_bpf->progs.nm_clat_egress_rawip,
self->priv.ifindex,
NULL);
if (!self->priv.p->clat_egress_link) {
libbpf_strerror(errno, buf, sizeof(buf));
_LOGW("clat: failed to attach the egress program: %s", buf);
return;
}
_LOGT("clat: program attached successfully");
}
/* Pass configuration to the BPF program */
memset(&clat_config, 0, sizeof(clat_config));
clat_config.local_v4.s_addr = self->priv.p->clat_address_4->addr;
clat_config.local_v6 = self->priv.p->clat_address_6.address;
clat_config.pref64 = *l3cd_pref64;
clat_config.pref64_len = l3cd_pref64_plen;
self->priv.p->clat_bpf->bss->config = clat_config;
if (self->priv.p->clat_socket < 0) {
self->priv.p->clat_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (self->priv.p->clat_socket < 0) {
_LOGW("clat: couldn't create the socket: %s", nm_strerror_native(errno));
}
}
if (self->priv.p->clat_socket >= 0) {
err = setsockopt(self->priv.p->clat_socket,
SOL_SOCKET,
SO_BINDTOIFINDEX,
&self->priv.ifindex,
sizeof(self->priv.ifindex));
if (err < 0) {
_LOGW("clat: couldn't bind the socket: %s", nm_strerror_native(errno));
}
}
v6_changed =
(self->priv.p->clat_address_6_valid != self->priv.p->clat_address_6_committed_valid)
|| (self->priv.p->clat_address_6_valid && self->priv.p->clat_address_6_committed_valid
&& memcmp(&self->priv.p->clat_address_6.address,
&self->priv.p->clat_address_6_committed.address,
sizeof(self->priv.p->clat_address_6_committed.address)));
if (self->priv.p->clat_socket > 0 && v6_changed) {
struct ipv6_mreq mreq = {.ipv6mr_interface = self->priv.ifindex};
if (self->priv.p->clat_address_6_committed_valid) {
mreq.ipv6mr_multiaddr = self->priv.p->clat_address_6_committed.address;
err = setsockopt(self->priv.p->clat_socket,
SOL_IPV6,
IPV6_LEAVE_ANYCAST,
&mreq,
sizeof(mreq));
if (err < 0) {
_LOGW("clat: couldn't leave the anycast group: %s", nm_strerror_native(errno));
}
}
if (self->priv.p->clat_address_6_valid) {
/* As per draft-ietf-v6ops-claton-14, hosts must perform duplicate
* addresses detection (DAD) on the generated CLAT IPv6 address. This is
* necessary not only to avoid address collisions but also because some
* networks drop traffic from addresses that have not done DAD.
* Since doing true DAD adds complexity, adopt the same approach as
* Android: start DAD by sending a neighbor solicitation and don't wait
* for any reply. This avoids the problem with dropped traffic; it
* doesn't help with collisions, but collisions are anyway very unlikely
* because the interface identifier is a random 64-bit value.
*/
nm_utils_ipv6_dad_send(&self->priv.p->clat_address_6.address,
self->priv.ifindex,
pllink ? pllink->arptype : ARPHRD_ETHER);
mreq.ipv6mr_multiaddr = self->priv.p->clat_address_6.address;
err = setsockopt(self->priv.p->clat_socket,
SOL_IPV6,
IPV6_JOIN_ANYCAST,
&mreq,
sizeof(mreq));
if (err < 0) {
_LOGW("clat: couldn't join the anycast group: %s", nm_strerror_native(errno));
}
}
}
} else {
if (self->priv.p->clat_bpf) {
_l3_clat_destroy(self);
}
/* Committed will get cleaned up below */
self->priv.p->clat_address_6_valid = FALSE;
nm_clear_fd(&self->priv.p->clat_socket);
/* Deallocate the v4 address. Committed address will get cleaned up below,
but we need to make sure there's no double-free */
if (self->priv.p->clat_address_4 != self->priv.p->clat_address_4_committed) {
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
} else {
self->priv.p->clat_address_4 = NULL;
}
}
/* Record the new state */
if (self->priv.p->clat_address_4_committed != self->priv.p->clat_address_4) {
nm_clear_pointer(&self->priv.p->clat_address_4_committed, nm_netns_ip_reservation_release);
self->priv.p->clat_address_4_committed = self->priv.p->clat_address_4;
}
self->priv.p->clat_address_6_committed = self->priv.p->clat_address_6;
self->priv.p->clat_address_6_committed_valid = self->priv.p->clat_address_6_valid;
}
#endif /* HAVE_CLAT */
static void static void
_l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle) _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle)
{ {
@ -5461,6 +5980,10 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle)
_l3_acd_data_process_changes(self); _l3_acd_data_process_changes(self);
#if HAVE_CLAT
_l3_commit_pref64(self, commit_type);
#endif /* HAVE_CLAT */
nm_assert(self->priv.p->commit_reentrant_count == 1); nm_assert(self->priv.p->commit_reentrant_count == 1);
self->priv.p->commit_reentrant_count--; self->priv.p->commit_reentrant_count--;
@ -5842,6 +6365,10 @@ nm_l3cfg_init(NML3Cfg *self)
{ {
self->priv.p = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_L3CFG, NML3CfgPrivate); self->priv.p = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_L3CFG, NML3CfgPrivate);
#if HAVE_CLAT
self->priv.p->clat_socket = -1;
#endif /* HAVE_CLAT */
c_list_init(&self->priv.p->acd_lst_head); c_list_init(&self->priv.p->acd_lst_head);
c_list_init(&self->priv.p->acd_event_notify_lst_head); c_list_init(&self->priv.p->acd_event_notify_lst_head);
c_list_init(&self->priv.p->commit_type_lst_head); c_list_init(&self->priv.p->commit_type_lst_head);
@ -5950,6 +6477,14 @@ finalize(GObject *object)
if (changed) if (changed)
nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, FALSE); nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, FALSE);
#if HAVE_CLAT
self->priv.p->clat_address_4_committed = NULL;
nm_clear_pointer(&self->priv.p->clat_address_4, nm_netns_ip_reservation_release);
nm_clear_fd(&self->priv.p->clat_socket);
if (self->priv.p->clat_bpf) {
_l3_clat_destroy(self);
}
#endif
g_clear_object(&self->priv.netns); g_clear_object(&self->priv.netns);
g_clear_object(&self->priv.platform); g_clear_object(&self->priv.platform);
nm_clear_pointer(&self->priv.global_tracker, nmp_global_tracker_unref); nm_clear_pointer(&self->priv.global_tracker, nmp_global_tracker_unref);

View file

@ -178,7 +178,7 @@ typedef struct {
} platform_change; } platform_change;
struct { struct {
guint32 obj_type_flags; guint64 obj_type_flags;
} platform_change_on_idle; } platform_change_on_idle;
struct { struct {
@ -207,7 +207,7 @@ struct _NML3Cfg {
* NML3Cfg instance. We track some per-l3cfg-data that is only * NML3Cfg instance. We track some per-l3cfg-data that is only
* relevant to NMNetns here. */ * relevant to NMNetns here. */
struct { struct {
guint32 signal_pending_obj_type_flags; guint64 signal_pending_obj_type_flags;
CList signal_pending_lst; CList signal_pending_lst;
CList ecmp_track_ifindex_lst_head; CList ecmp_track_ifindex_lst_head;
} internal_netns; } internal_netns;
@ -223,7 +223,7 @@ NML3Cfg *nm_l3cfg_new(NMNetns *netns, int ifindex);
gboolean nm_l3cfg_is_ready(NML3Cfg *self); gboolean nm_l3cfg_is_ready(NML3Cfg *self);
void _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint32 obj_type_flags); void _nm_l3cfg_notify_platform_change_on_idle(NML3Cfg *self, guint64 obj_type_flags);
void _nm_l3cfg_notify_platform_change(NML3Cfg *self, void _nm_l3cfg_notify_platform_change(NML3Cfg *self,
NMPlatformSignalChangeType change_type, NMPlatformSignalChangeType change_type,

View file

@ -207,6 +207,7 @@ _ecmp_track_sort_lst_cmp(const CList *a, const CList *b, const void *user_data)
NM_CMP_FIELD(route_a, route_b, ifindex); NM_CMP_FIELD(route_a, route_b, ifindex);
NM_CMP_FIELD(route_b, route_a, weight); NM_CMP_FIELD(route_b, route_a, weight);
NM_CMP_DIRECT(htonl(route_a->gateway), htonl(route_b->gateway)); NM_CMP_DIRECT(htonl(route_a->gateway), htonl(route_b->gateway));
NM_CMP_DIRECT(route_a->r_rtm_flags & RTNH_F_ONLINK, route_b->r_rtm_flags & RTNH_F_ONLINK);
return nm_assert_unreachable_val( return nm_assert_unreachable_val(
nm_platform_ip4_route_cmp(route_a, route_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)); nm_platform_ip4_route_cmp(route_a, route_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID));
@ -275,9 +276,10 @@ _ecmp_track_init_merged_obj(EcmpTrackEcmpid *track_ecmpid, const NMPObject **out
NMPlatformIP4RtNextHop *nh = (gpointer) &obj_new->_ip4_route.extra_nexthops[i - 1]; NMPlatformIP4RtNextHop *nh = (gpointer) &obj_new->_ip4_route.extra_nexthops[i - 1];
*nh = (NMPlatformIP4RtNextHop) { *nh = (NMPlatformIP4RtNextHop) {
.ifindex = r->ifindex, .ifindex = r->ifindex,
.gateway = r->gateway, .gateway = r->gateway,
.weight = r->weight, .weight = r->weight,
.rtnh_flags = r->r_rtm_flags & RTNH_F_ONLINK,
}; };
} }
i++; i++;
@ -574,8 +576,8 @@ notify_watcher:
typedef struct { typedef struct {
const char *name; const char *name;
guint32 start_addr; /* host byte order */ guint32 start_addr; /* host byte order */
guint prefix_len; guint range_plen;
guint num_addrs; guint addr_plen;
gboolean allow_reuse; gboolean allow_reuse;
} IPReservationTypeDesc; } IPReservationTypeDesc;
@ -583,11 +585,19 @@ static const IPReservationTypeDesc ip_reservation_types[_NM_NETNS_IP_RESERVATION
[NM_NETNS_IP_RESERVATION_TYPE_SHARED4] = [NM_NETNS_IP_RESERVATION_TYPE_SHARED4] =
{ {
.name = "shared-ip4", .name = "shared-ip4",
.start_addr = 0x0a2a0001, /* 10.42.0.1 */ .start_addr = 0x0a2a0001, /* 10.42.{0-255}.1/24 */
.prefix_len = 24, .range_plen = 16,
.num_addrs = 256, .addr_plen = 24,
.allow_reuse = TRUE, .allow_reuse = TRUE,
}, },
[NM_NETNS_IP_RESERVATION_TYPE_CLAT] =
{
.name = "clat",
.start_addr = 0xc0000005, /* 192.0.0.{5-7,0-4}/32 */
.range_plen = 29,
.addr_plen = 32,
.allow_reuse = FALSE,
},
}; };
NMNetnsIPReservation * NMNetnsIPReservation *
@ -613,13 +623,23 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
g_object_ref(self); g_object_ref(self);
} else { } else {
guint32 count; guint32 count;
guint32 base_network;
guint32 host_mask;
guint32 increment;
nm_assert(g_hash_table_size(*table) > 0); nm_assert(g_hash_table_size(*table) > 0);
nm_assert(desc->prefix_len > 0 && desc->prefix_len <= 32); nm_assert(desc->range_plen < 32);
nm_assert(desc->addr_plen > 0 && desc->addr_plen <= 32);
nm_assert(desc->addr_plen > desc->range_plen);
base_network = desc->start_addr & ~(0xFFFFFFFFu >> desc->range_plen);
host_mask = 0xFFFFFFFFu >> desc->range_plen;
increment = 1 << (32 - desc->addr_plen);
count = 0u; count = 0u;
for (;;) { for (;;) {
addr = htonl(desc->start_addr + (count << (32 - desc->prefix_len))); addr = htonl(base_network
+ ((base_network + (desc->start_addr + count * increment)) & host_mask));
res = g_hash_table_lookup(*table, &addr); res = g_hash_table_lookup(*table, &addr);
if (!res) if (!res)
@ -627,7 +647,7 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
count++; count++;
if (count >= desc->num_addrs) { if (count >= 1 << (desc->addr_plen - desc->range_plen)) {
if (!desc->allow_reuse) { if (!desc->allow_reuse) {
_LOGE("%s: ran out of IP addresses", desc->name); _LOGE("%s: ran out of IP addresses", desc->name);
return NULL; return NULL;
@ -637,12 +657,12 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
_LOGE("%s: ran out of IP addresses. Reuse %s/%u", _LOGE("%s: ran out of IP addresses. Reuse %s/%u",
desc->name, desc->name,
nm_inet4_ntop(res->addr, buf), nm_inet4_ntop(res->addr, buf),
desc->prefix_len); desc->addr_plen);
} else { } else {
_LOGD("%s: reserved IP address %s/%u (duplicate)", _LOGD("%s: reserved IP address %s/%u (duplicate)",
desc->name, desc->name,
nm_inet4_ntop(res->addr, buf), nm_inet4_ntop(res->addr, buf),
desc->prefix_len); desc->addr_plen);
} }
res->_ref_count++; res->_ref_count++;
return res; return res;
@ -663,7 +683,7 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
_LOGD("%s: reserved IP address %s/%u", _LOGD("%s: reserved IP address %s/%u",
desc->name, desc->name,
nm_inet4_ntop(res->addr, buf), nm_inet4_ntop(res->addr, buf),
desc->prefix_len); desc->addr_plen);
return res; return res;
} }
@ -695,7 +715,7 @@ nm_netns_ip_reservation_release(NMNetnsIPReservation *res)
_LOGD("%s: release IP address reservation %s/%u (%d more references held)", _LOGD("%s: release IP address reservation %s/%u (%d more references held)",
desc->name, desc->name,
nm_inet4_ntop(res->addr, buf), nm_inet4_ntop(res->addr, buf),
desc->prefix_len, desc->addr_plen,
res->_ref_count); res->_ref_count);
return; return;
} }
@ -706,7 +726,7 @@ nm_netns_ip_reservation_release(NMNetnsIPReservation *res)
_LOGD("%s: release IP address reservation %s/%u", _LOGD("%s: release IP address reservation %s/%u",
desc->name, desc->name,
nm_inet4_ntop(res->addr, buf), nm_inet4_ntop(res->addr, buf),
desc->prefix_len); desc->addr_plen);
if (g_hash_table_size(*table) == 0) { if (g_hash_table_size(*table) == 0) {
nm_clear_pointer(table, g_hash_table_unref); nm_clear_pointer(table, g_hash_table_unref);

View file

@ -43,6 +43,7 @@ NML3Cfg *nm_netns_l3cfg_acquire(NMNetns *netns, int ifindex);
typedef enum { typedef enum {
NM_NETNS_IP_RESERVATION_TYPE_SHARED4, NM_NETNS_IP_RESERVATION_TYPE_SHARED4,
NM_NETNS_IP_RESERVATION_TYPE_CLAT,
_NM_NETNS_IP_RESERVATION_TYPE_NUM, _NM_NETNS_IP_RESERVATION_TYPE_NUM,
} NMNetnsIPReservationType; } NMNetnsIPReservationType;

View file

@ -1230,7 +1230,7 @@ out:
} }
gboolean gboolean
nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert) nmtstp_check_platform_full(NMPlatform *platform, guint64 obj_type_flags, gboolean do_assert)
{ {
static const NMPObjectType obj_types[] = { static const NMPObjectType obj_types[] = {
NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ADDRESS,
@ -1265,7 +1265,7 @@ nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolea
for (i_obj_types = 0; i_obj_types < (int) G_N_ELEMENTS(obj_types); i_obj_types++) { for (i_obj_types = 0; i_obj_types < (int) G_N_ELEMENTS(obj_types); i_obj_types++) {
const NMPObjectType obj_type = obj_types[i_obj_types]; const NMPObjectType obj_type = obj_types[i_obj_types];
const guint32 i_obj_type_flags = nmp_object_type_to_flags(obj_type); const guint64 i_obj_type_flags = nmp_object_type_to_flags(obj_type);
gs_unref_ptrarray GPtrArray *arr1 = NULL; gs_unref_ptrarray GPtrArray *arr1 = NULL;
gs_unref_ptrarray GPtrArray *arr2 = NULL; gs_unref_ptrarray GPtrArray *arr2 = NULL;
NMPLookup lookup; NMPLookup lookup;
@ -1408,7 +1408,7 @@ nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolea
} }
void void
nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags) nmtstp_check_platform(NMPlatform *platform, guint64 obj_type_flags)
{ {
if (!nmtstp_check_platform_full(platform, obj_type_flags, FALSE)) { if (!nmtstp_check_platform_full(platform, obj_type_flags, FALSE)) {
/* It's unclear why this failure sometimes happens. It happens /* It's unclear why this failure sometimes happens. It happens
@ -2393,6 +2393,61 @@ nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *na
return pllink; return pllink;
} }
const NMPlatformLink *
nmtstp_link_geneve_add(NMPlatform *platform,
int external_command,
const char *name,
const NMPlatformLnkGeneve *lnk)
{
const NMPlatformLink *pllink = NULL;
int success;
g_assert(nm_utils_ifname_valid_kernel(name, NULL));
g_assert(lnk->remote || !IN6_IS_ADDR_UNSPECIFIED(&lnk->remote6));
external_command = nmtstp_run_command_check_external(external_command);
_init_platform(&platform, external_command);
if (external_command) {
char remote[NM_INET_ADDRSTRLEN];
char remote6[NM_INET_ADDRSTRLEN];
char str_ttl[30];
if (lnk->remote)
nm_inet4_ntop(lnk->remote, remote);
else
remote[0] = '\0';
if (memcmp(&lnk->remote6, &in6addr_any, sizeof(in6addr_any)))
nm_inet6_ntop(&lnk->remote6, remote6);
else
remote6[0] = '\0';
success = !nmtstp_run_command(
"ip link add %s type geneve id %u remote %s %s tos %02x dstport %u%s",
name,
lnk->id,
remote[0] ? remote : remote6,
lnk->ttl > 0 ? nm_sprintf_buf(str_ttl, "ttl %u", lnk->ttl & 0xff)
: lnk->ttl == 0 ? "ttl auto"
: "ttl inherit",
lnk->tos,
lnk->dst_port,
lnk->df == 1 ? " df set "
: lnk->df == 2 ? " df inherit "
: "");
if (success)
pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_GENEVE, 100);
} else
success = NMTST_NM_ERR_SUCCESS(nm_platform_link_geneve_add(platform, name, lnk, &pllink));
_assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_GENEVE);
return pllink;
}
const NMPlatformLink * const NMPlatformLink *
nmtstp_link_gre_add(NMPlatform *platform, nmtstp_link_gre_add(NMPlatform *platform,
int external_command, int external_command,
@ -3024,8 +3079,6 @@ nmtstp_link_vxlan_add(NMPlatform *platform,
return pllink; return pllink;
} }
/*****************************************************************************/
const NMPlatformLink * const NMPlatformLink *
nmtstp_link_get_typed(NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type) nmtstp_link_get_typed(NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type)
{ {

View file

@ -140,9 +140,9 @@ int nmtstp_run_command(const char *format, ...) _nm_printf(1, 2);
/*****************************************************************************/ /*****************************************************************************/
gboolean gboolean
nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert); nmtstp_check_platform_full(NMPlatform *platform, guint64 obj_type_flags, gboolean do_assert);
void nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags); void nmtstp_check_platform(NMPlatform *platform, guint64 obj_type_flags);
/*****************************************************************************/ /*****************************************************************************/
@ -474,6 +474,10 @@ const NMPlatformLink *nmtstp_link_veth_add(NMPlatform *platform,
const char *peer); const char *peer);
const NMPlatformLink * const NMPlatformLink *
nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name); nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name);
const NMPlatformLink *nmtstp_link_geneve_add(NMPlatform *platform,
int external_command,
const char *name,
const NMPlatformLnkGeneve *lnk);
const NMPlatformLink *nmtstp_link_gre_add(NMPlatform *platform, const NMPlatformLink *nmtstp_link_gre_add(NMPlatform *platform,
int external_command, int external_command,
const char *name, const char *name,

View file

@ -1388,10 +1388,11 @@ test_software_detect(gconstpointer user_data)
const gboolean ext = test_data->external_command; const gboolean ext = test_data->external_command;
NMPlatformLnkBridge lnk_bridge = {}; NMPlatformLnkBridge lnk_bridge = {};
NMPlatformLnkTun lnk_tun; NMPlatformLnkTun lnk_tun;
NMPlatformLnkGre lnk_gre = {}; NMPlatformLnkGeneve lnk_geneve = {};
NMPlatformLnkVti lnk_vti = {}; NMPlatformLnkGre lnk_gre = {};
NMPlatformLnkVti6 lnk_vti6 = {}; NMPlatformLnkVti lnk_vti = {};
nm_auto_close int tun_fd = -1; NMPlatformLnkVti6 lnk_vti6 = {};
nm_auto_close int tun_fd = -1;
gboolean module_loaded; gboolean module_loaded;
nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME); nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME);
@ -1434,6 +1435,31 @@ test_software_detect(gconstpointer user_data)
g_error("Failed adding Bridge interface"); g_error("Failed adding Bridge interface");
break; break;
case NM_LINK_TYPE_GENEVE:
{
switch (test_data->test_mode) {
case 0:
lnk_geneve.id = 42;
lnk_geneve.remote = nmtst_inet4_from_string("192.168.1.100");
lnk_geneve.ttl = 64;
lnk_geneve.tos = 0;
lnk_geneve.dst_port = 6081;
lnk_geneve.df = 0;
break;
case 1:
lnk_geneve.id = 12345;
lnk_geneve.remote6 = nmtst_inet6_from_string("2001:db8::1");
lnk_geneve.ttl = 128;
lnk_geneve.tos = 16;
lnk_geneve.dst_port = 6082;
lnk_geneve.df = 1;
break;
}
g_assert(nmtstp_link_geneve_add(NULL, ext, DEVICE_NAME, &lnk_geneve));
break;
}
case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_GRE:
module_loaded = nmtstp_ensure_module("ip_gre"); module_loaded = nmtstp_ensure_module("ip_gre");
@ -2208,6 +2234,34 @@ test_software_detect(gconstpointer user_data)
} }
break; break;
} }
case NM_LINK_TYPE_GENEVE:
{
const NMPlatformLnkGeneve *plnk = &lnk->lnk_geneve;
g_assert(plnk == nm_platform_link_get_lnk_geneve(NM_PLATFORM_GET, ifindex, NULL));
switch (test_data->test_mode) {
case 0:
g_assert_cmpint(plnk->id, ==, 42);
nmtst_assert_ip4_address(plnk->remote, "192.168.1.100");
nmtst_assert_ip6_address(&plnk->remote6, "::");
g_assert_cmpint(plnk->ttl, ==, 64);
g_assert_cmpint(plnk->tos, ==, 0);
g_assert_cmpint(plnk->dst_port, ==, 6081);
g_assert_cmpint(plnk->df, ==, 0);
break;
case 1:
g_assert_cmpint(plnk->id, ==, 12345);
nmtst_assert_ip4_address(plnk->remote, "0.0.0.0");
nmtst_assert_ip6_address(&plnk->remote6, "2001:db8::1");
g_assert_cmpint(plnk->ttl, ==, 128);
g_assert_cmpint(plnk->tos, ==, 16);
g_assert_cmpint(plnk->dst_port, ==, 6082);
g_assert_cmpint(plnk->df, ==, 1);
break;
}
break;
}
case NM_LINK_TYPE_WIREGUARD: case NM_LINK_TYPE_WIREGUARD:
{ {
const NMPlatformLnkWireGuard *plnk = &lnk->lnk_wireguard; const NMPlatformLnkWireGuard *plnk = &lnk->lnk_wireguard;
@ -4143,6 +4197,8 @@ _nmtstp_setup_tests(void)
g_test_add_func("/link/external", test_external); g_test_add_func("/link/external", test_external);
test_software_detect_add("/link/software/detect/bridge", NM_LINK_TYPE_BRIDGE, 0); test_software_detect_add("/link/software/detect/bridge", NM_LINK_TYPE_BRIDGE, 0);
test_software_detect_add("/link/software/detect/geneve/0", NM_LINK_TYPE_GENEVE, 0);
test_software_detect_add("/link/software/detect/geneve/1", NM_LINK_TYPE_GENEVE, 1);
test_software_detect_add("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); test_software_detect_add("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0);
test_software_detect_add("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0); test_software_detect_add("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0);
test_software_detect_add("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0); test_software_detect_add("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0);

View file

@ -2460,6 +2460,78 @@ done:
} }
} }
/*****************************************************************************/
static void
test_ip4_rtnh_onlink(void)
{
/* Extra nexthops that differ only in rtnh_flags ONLINK should
* compare as different. */
NMPlatformIP4RtNextHop nh_a = {
.ifindex = 2,
.gateway = nmtst_inet4_from_string("10.10.10.10"),
.weight = 1,
.rtnh_flags = 0,
};
NMPlatformIP4RtNextHop nh_b = nh_a;
nh_b.rtnh_flags = RTNH_F_ONLINK;
g_assert_cmpint(nm_platform_ip4_rt_nexthop_cmp(&nh_a, &nh_b, TRUE), !=, 0);
g_assert_cmpint(nm_platform_ip4_rt_nexthop_cmp(&nh_a, &nh_b, FALSE), !=, 0);
nh_b.rtnh_flags = 0;
g_assert_cmpint(nm_platform_ip4_rt_nexthop_cmp(&nh_a, &nh_b, TRUE), ==, 0);
g_assert_cmpint(nm_platform_ip4_rt_nexthop_cmp(&nh_a, &nh_b, FALSE), ==, 0);
}
static void
test_ip4_route_onlink_per_nexthop(void)
{
NMPlatformIP4Route r_a = {};
NMPlatformIP4Route r_b;
/* Two single-hop routes that are identical, except for the onlink flag. */
r_a.ifindex = 1;
r_a.rt_source = NM_IP_CONFIG_SOURCE_USER;
r_a.network = nmtst_inet4_from_string("10.10.10.10");
r_a.plen = 24;
r_a.gateway = nmtst_inet4_from_string("10.10.10.1");
r_a.metric = 100;
r_a.n_nexthops = 1;
r_a.type_coerced = nm_platform_route_type_coerce(RTN_UNICAST);
r_a.scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE);
r_b = r_a;
r_b.r_rtm_flags = RTNH_F_ONLINK;
/* Onlink flag should result in the same ECMP_ID but different IDs. */
g_assert_cmpint(nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID),
==,
0);
g_assert_cmpint(nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID), !=, 0);
g_assert_cmpint(
nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY),
!=,
0);
g_assert_cmpint(nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL),
!=,
0);
r_b.r_rtm_flags = 0;
g_assert_cmpint(nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID), ==, 0);
g_assert_cmpint(nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID),
==,
0);
g_assert_cmpint(
nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY),
==,
0);
g_assert_cmpint(nm_platform_ip4_route_cmp(&r_a, &r_b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL),
==,
0);
}
/*****************************************************************************/ /*****************************************************************************/
NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
@ -2480,6 +2552,8 @@ _nmtstp_setup_tests(void)
nmtstp_env1_add_test_func_data(testpath, test_func, arg, 2, TRUE) nmtstp_env1_add_test_func_data(testpath, test_func, arg, 2, TRUE)
add_test_func("/route/ip4", test_ip4_route); add_test_func("/route/ip4", test_ip4_route);
add_test_func("/route/ip4_onlink_per_nexthop", test_ip4_route_onlink_per_nexthop);
add_test_func("/route/ip4_rtnh_onlink", test_ip4_rtnh_onlink);
add_test_func("/route/ip6", test_ip6_route); add_test_func("/route/ip6", test_ip6_route);
add_test_func("/route/ip4_metric0", test_ip4_route_metric0); add_test_func("/route/ip4_metric0", test_ip4_route_metric0);
add_test_func_data("/route/ip4_options/1", test_ip4_route_options, GINT_TO_POINTER(1)); add_test_func_data("/route/ip4_options/1", test_ip4_route_options, GINT_TO_POINTER(1));

View file

@ -1083,6 +1083,39 @@ _con_get_request_start_validated(NMAuthChain *chain,
_con_get_request_start_proceed(req, req->con.current_has_modify); _con_get_request_start_proceed(req, req->con.current_has_modify);
} }
static gboolean
_req_has_existing_secrets(Request *req)
{
GVariantIter iter;
const char *setting_name;
GVariant *setting_dict;
gboolean has;
if (!req->con.get.existing_secrets)
return FALSE;
nm_assert(g_variant_is_of_type(req->con.get.existing_secrets, NM_VARIANT_TYPE_CONNECTION));
g_variant_iter_init(&iter, req->con.get.existing_secrets);
while (g_variant_iter_next(&iter, "{&s@a{sv}}", &setting_name, &setting_dict)) {
GVariantIter setting_iter;
GVariant *val;
g_variant_iter_init(&setting_iter, setting_dict);
while (g_variant_iter_next(&setting_iter, "{&sv}", NULL, &val)) {
has = !g_variant_is_container(val) || g_variant_n_children(val) > 0;
g_variant_unref(val);
if (has) {
g_variant_unref(setting_dict);
return TRUE;
}
}
g_variant_unref(setting_dict);
}
return FALSE;
}
static void static void
_con_get_request_start(Request *req) _con_get_request_start(Request *req)
{ {
@ -1103,7 +1136,7 @@ _con_get_request_start(Request *req)
* unprivileged users. * unprivileged users.
*/ */
if ((req->con.get.flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) if ((req->con.get.flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)
&& (req->con.get.existing_secrets && (_req_has_existing_secrets(req)
|| _nm_connection_aggregate(req->con.connection, || _nm_connection_aggregate(req->con.connection,
NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS,
NULL))) { NULL))) {

View file

@ -781,7 +781,8 @@ validate_secret_flags(NMConnection *connection, GVariant *secrets, ForEachSecret
static gboolean static gboolean
secret_is_system_owned(NMSettingSecretFlags flags, gpointer user_data) secret_is_system_owned(NMSettingSecretFlags flags, gpointer user_data)
{ {
return !NM_FLAGS_HAS(flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED); return !NM_FLAGS_ANY(flags,
NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED);
} }
static void static void

View file

@ -77,7 +77,7 @@ get_full_file_path(const char *ifcfg_path, const char *file_path)
{ {
const char *base = file_path; const char *base = file_path;
gs_free char *dirname = NULL; gs_free char *dirname = NULL;
char *p; const char *p;
g_return_val_if_fail(ifcfg_path != NULL, NULL); g_return_val_if_fail(ifcfg_path != NULL, NULL);
g_return_val_if_fail(file_path != NULL, NULL); g_return_val_if_fail(file_path != NULL, NULL);
@ -4401,7 +4401,7 @@ make_wireless_setting(shvarFile *ifcfg, GError **error)
NMSettingWireless *s_wireless; NMSettingWireless *s_wireless;
const char *cvalue; const char *cvalue;
char *value = NULL; char *value = NULL;
gint64 chan = 0; guint64 chan = 0;
NMSettingMacRandomization mac_randomization; NMSettingMacRandomization mac_randomization;
NMSettingWirelessPowersave powersave = NM_SETTING_WIRELESS_POWERSAVE_DEFAULT; NMSettingWirelessPowersave powersave = NM_SETTING_WIRELESS_POWERSAVE_DEFAULT;
NMTernary ternary; NMTernary ternary;
@ -4502,7 +4502,7 @@ make_wireless_setting(shvarFile *ifcfg, GError **error)
value = svGetValueStr_cp(ifcfg, "CHANNEL"); value = svGetValueStr_cp(ifcfg, "CHANNEL");
if (value) { if (value) {
chan = _nm_utils_ascii_str_to_int64(value, 10, 1, 196, 0); chan = _nm_utils_ascii_str_to_int64(value, 10, 1, _NM_WIFI_CHANNEL_MAX, 0);
if (chan == 0) { if (chan == 0) {
g_set_error(error, g_set_error(error,
NM_SETTINGS_ERROR, NM_SETTINGS_ERROR,
@ -4518,44 +4518,47 @@ make_wireless_setting(shvarFile *ifcfg, GError **error)
value = svGetValueStr_cp(ifcfg, "BAND"); value = svGetValueStr_cp(ifcfg, "BAND");
if (value) { if (value) {
if (!strcmp(value, "a")) { if (!NM_IN_STRSET(value, "a", "bg", "6GHz")) {
if (chan && chan <= 14) {
g_set_error(error,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Band '%s' invalid for channel %u",
value,
(guint32) chan);
g_free(value);
goto error;
}
} else if (!strcmp(value, "bg")) {
if (chan && chan > 14) {
g_set_error(error,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Band '%s' invalid for channel %u",
value,
(guint32) chan);
g_free(value);
goto error;
}
} else {
g_set_error(error, g_set_error(error,
NM_SETTINGS_ERROR, NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid wireless band '%s'", "Band '%s' invalid",
value); value);
g_free(value); g_free(value);
goto error; goto error;
} }
if (chan) {
if (!nm_utils_wifi_is_channel_valid(chan, value)) {
g_set_error(error,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Band '%s' invalid for channel %u",
value,
(guint32) chan);
g_free(value);
goto error;
}
}
g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, value, NULL); g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, value, NULL);
g_free(value); g_free(value);
} else if (chan > 0) { } else if (chan > 0) {
if (chan > 14) if (chan > _NM_WIFI_CHANNEL_MAX_5GHZ) {
g_set_error(error,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Setting channel without band is ambiguous and deprecated. Not supported "
"for 6GHz.");
g_free(value);
goto error;
} else if (chan > _NM_WIFI_CHANNEL_MAX_2GHZ) {
PARSE_WARNING(
"Setting channel without band is ambiguous and deprecated. Assuming band 'a'.");
g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, "a", NULL); g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, "a", NULL);
else } else {
g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL); g_object_set(s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL);
}
} }
value = svGetValueStr_cp(ifcfg, "MTU"); value = svGetValueStr_cp(ifcfg, "MTU");

View file

@ -849,7 +849,7 @@ write_wireless_setting(NMConnection *connection,
GBytes *ssid; GBytes *ssid;
const guint8 *ssid_data; const guint8 *ssid_data;
gsize ssid_len; gsize ssid_len;
const char *mode, *bssid; const char *mode, *bssid, *band;
const char *device_mac, *cloned_mac; const char *device_mac, *cloned_mac;
guint32 mtu, chan, i; guint32 mtu, chan, i;
gboolean adhoc = FALSE, hex_ssid = FALSE; gboolean adhoc = FALSE, hex_ssid = FALSE;
@ -968,9 +968,11 @@ write_wireless_setting(NMConnection *connection,
chan = nm_setting_wireless_get_channel(s_wireless); chan = nm_setting_wireless_get_channel(s_wireless);
if (chan) { if (chan) {
svSetValueInt64(ifcfg, "CHANNEL", chan); svSetValueInt64(ifcfg, "CHANNEL", chan);
} else { }
/* Band only set if channel is not, since channel implies band */
svSetValueStr(ifcfg, "BAND", nm_setting_wireless_get_band(s_wireless)); band = nm_setting_wireless_get_band(s_wireless);
if (band) {
svSetValueStr(ifcfg, "BAND", band);
} }
bssid = nm_setting_wireless_get_bssid(s_wireless); bssid = nm_setting_wireless_get_bssid(s_wireless);
@ -3598,6 +3600,7 @@ do_write_construct(NMConnection *connection,
} else } else
route_ignore = FALSE; route_ignore = FALSE;
/* Unsupported properties */
if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))) { if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))) {
if (nm_setting_ip_config_get_dhcp_dscp(s_ip4)) { if (nm_setting_ip_config_get_dhcp_dscp(s_ip4)) {
set_error_unsupported(error, set_error_unsupported(error,
@ -3616,6 +3619,14 @@ do_write_construct(NMConnection *connection,
FALSE); FALSE);
return FALSE; return FALSE;
} }
if (nm_setting_ip4_config_get_clat(NM_SETTING_IP4_CONFIG(s_ip4))
!= NM_SETTING_IP4_CONFIG_CLAT_DEFAULT) {
set_error_unsupported(error,
connection,
NM_SETTING_IP4_CONFIG_SETTING_NAME "." NM_SETTING_IP4_CONFIG_CLAT,
FALSE);
return FALSE;
}
} }
write_ip4_setting(connection, write_ip4_setting(connection,

View file

@ -1,6 +1,7 @@
ESSID=MySSID ESSID=MySSID
MODE=Ap MODE=Ap
CHANNEL=196 CHANNEL=52
BAND=a
MAC_ADDRESS_RANDOMIZATION=default MAC_ADDRESS_RANDOMIZATION=default
AP_ISOLATION=yes AP_ISOLATION=yes
TYPE=Wireless TYPE=Wireless

View file

@ -0,0 +1,18 @@
ESSID="Test SSID"
MODE=Managed
BAND=6GHz
MAC_ADDRESS_RANDOMIZATION=default
TYPE=Wireless
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=default
NAME="Test Write Wi-Fi Band 6GHz"
UUID=${UUID}
ONBOOT=yes

View file

@ -13,6 +13,6 @@ IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=default IPV6_ADDR_GEN_MODE=default
NAME="Test Write Wi-Fi Band A" NAME="Test Write Wi-Fi Band A - 5GHz"
UUID=${UUID} UUID=${UUID}
ONBOOT=yes ONBOOT=yes

View file

@ -0,0 +1,13 @@
TYPE=Wireless
DEVICE=eth2
HWADDR=00:16:41:11:22:33
NM_CONTROLLED=yes
BOOTPROTO=dhcp
ESSID=blahblah
BAND=6GHz
MODE=Managed
RATE=auto
ONBOOT=yes
USERCTL=yes
PEERDNS=yes
IPV6INIT=no

View file

@ -0,0 +1,9 @@
TYPE=Wireless
DEVICE=eth2
HWADDR=00:16:41:11:22:33
BOOTPROTO=dhcp
ESSID=blahblah
CHANNEL=14
BAND=6GHz
MODE=Managed

View file

@ -3991,7 +3991,7 @@ test_write_wifi_band_a(void)
s_con = _nm_connection_new_setting(connection, NM_TYPE_SETTING_CONNECTION); s_con = _nm_connection_new_setting(connection, NM_TYPE_SETTING_CONNECTION);
g_object_set(s_con, g_object_set(s_con,
NM_SETTING_CONNECTION_ID, NM_SETTING_CONNECTION_ID,
"Test Write Wi-Fi Band A", "Test Write Wi-Fi Band A - 5GHz",
NM_SETTING_CONNECTION_UUID, NM_SETTING_CONNECTION_UUID,
nm_uuid_generate_random_str_a(), nm_uuid_generate_random_str_a(),
NM_SETTING_CONNECTION_TYPE, NM_SETTING_CONNECTION_TYPE,
@ -4012,7 +4012,7 @@ test_write_wifi_band_a(void)
_writer_new_connec_exp(connection, _writer_new_connec_exp(connection,
TEST_SCRATCH_DIR, TEST_SCRATCH_DIR,
TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_Band_A.cexpected", TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_Band_a.cexpected",
&testfile); &testfile);
f = _svOpenFile(testfile); f = _svOpenFile(testfile);
@ -4024,6 +4024,77 @@ test_write_wifi_band_a(void)
nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); nmtst_assert_connection_equals(connection, TRUE, reread, FALSE);
} }
static void
test_read_wifi_band_6ghz(void)
{
gs_unref_object NMConnection *connection = NULL;
NMSettingConnection *s_con;
NMSettingWireless *s_wifi;
connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-band-6ghz",
NULL,
TYPE_WIRELESS,
NULL);
s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION);
g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
==,
NM_SETTING_WIRELESS_SETTING_NAME);
s_wifi = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_WIRELESS);
g_assert_cmpstr(nm_setting_wireless_get_band(s_wifi), ==, "6GHz");
}
static void
test_write_wifi_band_6ghz(void)
{
nmtst_auto_unlinkfile char *testfile = NULL;
gs_unref_object NMConnection *connection = NULL;
gs_unref_object NMConnection *reread = NULL;
NMSettingConnection *s_con;
NMSettingWireless *s_wifi;
shvarFile *f;
gs_unref_bytes GBytes *ssid =
nmtst_gbytes_from_arr(0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44);
connection = nm_simple_connection_new();
s_con = _nm_connection_new_setting(connection, NM_TYPE_SETTING_CONNECTION);
g_object_set(s_con,
NM_SETTING_CONNECTION_ID,
"Test Write Wi-Fi Band 6GHz",
NM_SETTING_CONNECTION_UUID,
nm_uuid_generate_random_str_a(),
NM_SETTING_CONNECTION_TYPE,
NM_SETTING_WIRELESS_SETTING_NAME,
NULL);
s_wifi = _nm_connection_new_setting(connection, NM_TYPE_SETTING_WIRELESS);
g_object_set(s_wifi,
NM_SETTING_WIRELESS_SSID,
ssid,
NM_SETTING_WIRELESS_MODE,
"infrastructure",
NM_SETTING_WIRELESS_BAND,
"6GHz",
NULL);
nmtst_assert_connection_verifies(connection);
_writer_new_connec_exp(connection,
TEST_SCRATCH_DIR,
TEST_IFCFG_DIR "/ifcfg-Test_Write_WiFi_Band_6ghz.cexpected",
&testfile);
f = _svOpenFile(testfile);
_svGetValue_check(f, "BAND", "6GHz");
svCloseFile(f);
reread = _connection_from_file(testfile, NULL, TYPE_WIRELESS, NULL);
nmtst_assert_connection_equals(connection, TRUE, reread, FALSE);
}
static void static void
test_write_wifi_ap_mode(void) test_write_wifi_ap_mode(void)
{ {
@ -4055,7 +4126,7 @@ test_write_wifi_ap_mode(void)
NM_SETTING_WIRELESS_BAND, NM_SETTING_WIRELESS_BAND,
"a", "a",
NM_SETTING_WIRELESS_CHANNEL, NM_SETTING_WIRELESS_CHANNEL,
(guint) 196, (guint) 52,
NM_SETTING_WIRELESS_AP_ISOLATION, NM_SETTING_WIRELESS_AP_ISOLATION,
NM_TERNARY_TRUE, NM_TERNARY_TRUE,
NULL); NULL);
@ -4072,6 +4143,18 @@ test_write_wifi_ap_mode(void)
nmtst_assert_connection_equals(connection, TRUE, reread, FALSE); nmtst_assert_connection_equals(connection, TRUE, reread, FALSE);
} }
static void
test_read_wifi_band_6ghz_channel_mismatch(void)
{
gs_free_error GError *error = NULL;
_connection_from_file_fail(TEST_IFCFG_DIR "/ifcfg-test-wifi-band-6ghz-channel-mismatch",
NULL,
TYPE_WIRELESS,
&error);
g_assert_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION);
}
static void static void
test_read_wifi_band_a_channel_mismatch(void) test_read_wifi_band_a_channel_mismatch(void)
{ {
@ -10680,6 +10763,9 @@ main(int argc, char **argv)
test_read_wifi_band_a_channel_mismatch); test_read_wifi_band_a_channel_mismatch);
g_test_add_func(TPATH "wifi/read-band-bg-channel-mismatch", g_test_add_func(TPATH "wifi/read-band-bg-channel-mismatch",
test_read_wifi_band_bg_channel_mismatch); test_read_wifi_band_bg_channel_mismatch);
g_test_add_func(TPATH "wifi/read-band-6ghz", test_read_wifi_band_6ghz);
g_test_add_func(TPATH "wifi/read-band-6ghz-channel-mismatch",
test_read_wifi_band_6ghz_channel_mismatch);
g_test_add_func(TPATH "wifi/read-hidden", test_read_wifi_hidden); g_test_add_func(TPATH "wifi/read-hidden", test_read_wifi_hidden);
nmtst_add_test_func(TPATH "wifi/read-mac-random-always", nmtst_add_test_func(TPATH "wifi/read-mac-random-always",
@ -10852,6 +10938,7 @@ main(int argc, char **argv)
test_write_wifi_wpa_then_wep_with_perms); test_write_wifi_wpa_then_wep_with_perms);
g_test_add_func(TPATH "wifi/write-hidden", test_write_wifi_hidden); g_test_add_func(TPATH "wifi/write-hidden", test_write_wifi_hidden);
g_test_add_func(TPATH "wifi/write-band-a", test_write_wifi_band_a); g_test_add_func(TPATH "wifi/write-band-a", test_write_wifi_band_a);
g_test_add_func(TPATH "wifi/write-band-6ghz", test_write_wifi_band_6ghz);
g_test_add_func(TPATH "wifi/write-ap-mode", test_write_wifi_ap_mode); g_test_add_func(TPATH "wifi/write-ap-mode", test_write_wifi_ap_mode);
g_test_add_func(TPATH "s390/read-qeth-static", test_read_wired_qeth_static); g_test_add_func(TPATH "s390/read-qeth-static", test_read_wired_qeth_static);

View file

@ -373,14 +373,24 @@ nm_supplicant_config_get_blobs(NMSupplicantConfig *self)
} }
static const char * static const char *
wifi_freqs_to_string(gboolean bg_band) wifi_freqs_to_string(const char *band)
{ {
static const char *str_2ghz = NULL; static const char *str_2ghz = NULL;
static const char *str_5ghz = NULL; static const char *str_5ghz = NULL;
static const char *str_6ghz = NULL;
const char **f_p; const char **f_p;
const char *f; const char *f;
f_p = bg_band ? &str_2ghz : &str_5ghz; if (nm_streq0(band, "a"))
f_p = &str_5ghz;
else if (nm_streq0(band, "bg"))
f_p = &str_2ghz;
else if (nm_streq0(band, "6GHz"))
f_p = &str_6ghz;
else {
nm_assert_not_reached();
return NULL;
}
again: again:
f = g_atomic_pointer_get(f_p); f = g_atomic_pointer_get(f_p);
@ -390,7 +400,13 @@ again:
const guint *freqs; const guint *freqs;
int i; int i;
freqs = bg_band ? nm_utils_wifi_2ghz_freqs() : nm_utils_wifi_5ghz_freqs(); if (f_p == &str_2ghz)
freqs = nm_utils_wifi_2ghz_freqs();
else if (f_p == &str_5ghz)
freqs = nm_utils_wifi_5ghz_freqs();
else
freqs = nm_utils_wifi_6ghz_freqs();
for (i = 0; freqs[i]; i++) { for (i = 0; freqs[i]; i++) {
if (i > 0) if (i > 0)
nm_str_buf_append_c(&strbuf, ' '); nm_str_buf_append_c(&strbuf, ' ');
@ -533,38 +549,47 @@ get_ap_params(guint freq,
guint channel; guint channel;
guint center_channel = 0; guint center_channel = 0;
if (freq < 5000) {
/* the setting is not valid */
nm_assert_not_reached();
return;
}
/* Determine the center channel according to the table at /* Determine the center channel according to the table at
* https://en.wikipedia.org/wiki/List_of_WLAN_channels */ * https://en.wikipedia.org/wiki/List_of_WLAN_channels */
channel = (freq - 5000) / 5; if (freq > 5950) {
/* 6 GHz */
channel = (freq - 5950) / 5;
channel = ((channel - 1) / 16) * 16 + 7;
if (channel >= 36 && channel <= 48)
center_channel = 42;
else if (channel >= 52 && channel <= 64)
center_channel = 58;
else if (channel >= 100 && channel <= 112)
center_channel = 106;
else if (channel >= 116 && channel <= 128)
center_channel = 122;
else if (channel >= 132 && channel <= 144)
center_channel = 138;
else if (channel >= 149 && channel <= 161)
center_channel = 155;
else if (channel >= 165 && channel <= 177)
center_channel = 171;
if (center_channel) {
*out_ht40 = 1; *out_ht40 = 1;
*out_max_oper_chwidth = 1; *out_max_oper_chwidth = 1;
*out_center_freq = 5000 + 5 * center_channel; *out_center_freq = 5950 + 5 * channel;
} } else {
/* 5 GHz */
if (freq < 5000) {
/* the setting is not valid */
nm_assert_not_reached();
return;
}
channel = (freq - 5000) / 5;
if (channel >= 36 && channel <= 48)
center_channel = 42;
else if (channel >= 52 && channel <= 64)
center_channel = 58;
else if (channel >= 100 && channel <= 112)
center_channel = 106;
else if (channel >= 116 && channel <= 128)
center_channel = 122;
else if (channel >= 132 && channel <= 144)
center_channel = 138;
else if (channel >= 149 && channel <= 161)
center_channel = 155;
else if (channel >= 165 && channel <= 177)
center_channel = 171;
if (center_channel) {
*out_ht40 = 1;
*out_max_oper_chwidth = 1;
*out_center_freq = 5000 + 5 * center_channel;
}
}
return; return;
} }
@ -711,10 +736,7 @@ nm_supplicant_config_add_setting_wireless(NMSupplicantConfig *self,
} else { } else {
const char *freqs = NULL; const char *freqs = NULL;
if (nm_streq(band, "a")) freqs = wifi_freqs_to_string(band);
freqs = wifi_freqs_to_string(FALSE);
else if (nm_streq(band, "bg"))
freqs = wifi_freqs_to_string(TRUE);
if (freqs if (freqs
&& !nm_supplicant_config_add_option(self, && !nm_supplicant_config_add_option(self,

View file

@ -67,6 +67,7 @@ enum {
GROUP_STARTED, /* a new Group (interface) was created */ GROUP_STARTED, /* a new Group (interface) was created */
GROUP_FINISHED, /* a Group (interface) has been finished */ GROUP_FINISHED, /* a Group (interface) has been finished */
PSK_MISMATCH, /* supplicant reported incorrect PSK */ PSK_MISMATCH, /* supplicant reported incorrect PSK */
SAE_MISMATCH, /* supplicant reported incorrect SAE Password */
LAST_SIGNAL LAST_SIGNAL
}; };
@ -3237,6 +3238,11 @@ _signal_handle(NMSupplicantInterface *self,
g_signal_emit(self, signals[PSK_MISMATCH], 0); g_signal_emit(self, signals[PSK_MISMATCH], 0);
return; return;
} }
if (nm_streq(signal_name, "SaePasswordMismatch")) {
g_signal_emit(self, signals[SAE_MISMATCH], 0);
return;
}
return; return;
} }
@ -3879,4 +3885,13 @@ nm_supplicant_interface_class_init(NMSupplicantInterfaceClass *klass)
NULL, NULL,
G_TYPE_NONE, G_TYPE_NONE,
0); 0);
signals[SAE_MISMATCH] = g_signal_new(NM_SUPPLICANT_INTERFACE_SAE_MISMATCH,
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
} }

View file

@ -87,6 +87,7 @@ typedef enum {
#define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started" #define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started"
#define NM_SUPPLICANT_INTERFACE_GROUP_FINISHED "group-finished" #define NM_SUPPLICANT_INTERFACE_GROUP_FINISHED "group-finished"
#define NM_SUPPLICANT_INTERFACE_PSK_MISMATCH "wpa-psk-mismatch" #define NM_SUPPLICANT_INTERFACE_PSK_MISMATCH "wpa-psk-mismatch"
#define NM_SUPPLICANT_INTERFACE_SAE_MISMATCH "wpa-sae-password-mismatch"
typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass; typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass;

View file

@ -6,6 +6,7 @@
#include "src/core/nm-default-daemon.h" #include "src/core/nm-default-daemon.h"
#include "nm-supplicant-settings-verify.h" #include "nm-supplicant-settings-verify.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -71,7 +72,7 @@ static const struct Opt opt_table[] = {
OPT_BYTES("engine_id", 0), OPT_BYTES("engine_id", 0),
OPT_INT("fragment_size", 1, 2000), OPT_INT("fragment_size", 1, 2000),
OPT_KEYWORD("freq_list", NULL), OPT_KEYWORD("freq_list", NULL),
OPT_INT("frequency", 2412, 5825), OPT_INT("frequency", _NM_WIFI_FREQ_MIN, _NM_WIFI_FREQ_MAX),
OPT_KEYWORD("group", NM_MAKE_STRV("CCMP", "TKIP", "WEP104", "WEP40", "GCMP-256", )), OPT_KEYWORD("group", NM_MAKE_STRV("CCMP", "TKIP", "WEP104", "WEP40", "GCMP-256", )),
OPT_INT("ht40", 0, 1), OPT_INT("ht40", 0, 1),
OPT_BYTES("identity", 0), OPT_BYTES("identity", 0),
@ -212,18 +213,19 @@ validate_type_utf8(const struct Opt *opt, const char *value, const guint32 len)
} }
static gboolean static gboolean
validate_type_keyword(const struct Opt *opt, const char *value, const guint32 len) validate_type_keyword(const struct Opt *opt, const char *value_in, const guint32 len)
{ {
gs_free char *value_free = NULL; gs_free char *value_free = NULL;
char *value;
nm_assert(opt); nm_assert(opt);
nm_assert(value); nm_assert(value_in);
/* Allow everything */ /* Allow everything */
if (!opt->str_allowed) if (!opt->str_allowed)
return TRUE; return TRUE;
value = nm_strndup_a(300, value, len, &value_free); value = nm_strndup_a(300, value_in, len, &value_free);
/* validate each space-separated word in 'value' */ /* validate each space-separated word in 'value' */

View file

@ -311,6 +311,108 @@ test_config_override(void)
g_assert_cmpstr(plugins[3], ==, "delta"); g_assert_cmpstr(plugins[3], ==, "delta");
} }
static void
test_config_managed(void)
{
NMConfig *config;
const char *CONFIG_USER = BUILD_DIR "/test-config-managed.conf";
const char *CONFIG_INTERN = BUILD_DIR "/test-config-managed-intern.conf";
NMDevice *dev;
gs_free char *group_by_name = NULL;
const char *ifname, *group_by_mac;
NMTernary managed;
gboolean by_mac;
GKeyFile *kf = nm_config_create_keyfile();
dev = nm_test_device_new("11:11:11:11:11:11");
ifname = nm_device_get_iface(dev);
group_by_name =
g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s", ifname);
group_by_mac = NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-11-11-11-11-11-11";
g_assert(g_file_set_contents(CONFIG_USER, "", 0, NULL));
g_assert(g_file_set_contents(CONFIG_INTERN, "", 0, NULL));
config = setup_config(NULL, CONFIG_USER, CONFIG_INTERN, NULL, "/no/such/dir", "", NULL);
g_assert(nm_config_get_device_managed(config, dev, &managed, NULL, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_DEFAULT);
/* Matching by name */
NMTST_EXPECT_NM_INFO("config: signal: *");
g_assert(nm_config_set_device_managed(config, dev, NM_TERNARY_TRUE, FALSE, NULL));
g_assert(nm_config_get_device_managed(config, dev, &managed, &by_mac, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_TRUE);
g_assert_false(by_mac);
g_key_file_load_from_file(kf, CONFIG_INTERN, G_KEY_FILE_NONE, NULL);
g_assert_false(g_key_file_has_key(kf, group_by_mac, "managed", NULL));
g_assert_true(g_key_file_has_key(kf, group_by_name, "managed", NULL));
g_assert_cmpint(g_key_file_get_integer(kf, group_by_name, "managed", NULL), ==, 1);
NMTST_EXPECT_NM_INFO("config: signal: *");
g_assert(nm_config_set_device_managed(config, dev, NM_TERNARY_FALSE, FALSE, NULL));
g_assert(nm_config_get_device_managed(config, dev, &managed, &by_mac, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_FALSE);
g_assert_false(by_mac);
g_key_file_load_from_file(kf, CONFIG_INTERN, G_KEY_FILE_NONE, NULL);
g_assert_false(g_key_file_has_key(kf, group_by_mac, "managed", NULL));
g_assert_true(g_key_file_has_key(kf, group_by_name, "managed", NULL));
g_assert_cmpint(g_key_file_get_integer(kf, group_by_name, "managed", NULL), ==, 0);
/* Matching by MAC address */
NMTST_EXPECT_NM_INFO("config: signal: *");
g_assert(nm_config_set_device_managed(config, dev, NM_TERNARY_TRUE, TRUE, NULL));
g_assert(nm_config_get_device_managed(config, dev, &managed, &by_mac, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_TRUE);
g_assert_true(by_mac);
g_key_file_load_from_file(kf, CONFIG_INTERN, G_KEY_FILE_NONE, NULL);
g_assert_false(g_key_file_has_key(kf, group_by_name, "managed", NULL));
g_assert_true(g_key_file_has_key(kf, group_by_mac, "managed", NULL));
g_assert_cmpint(g_key_file_get_integer(kf, group_by_mac, "managed", NULL), ==, 1);
NMTST_EXPECT_NM_INFO("config: signal: *");
g_assert(nm_config_set_device_managed(config, dev, NM_TERNARY_FALSE, TRUE, NULL));
g_assert(nm_config_get_device_managed(config, dev, &managed, &by_mac, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_FALSE);
g_assert_true(by_mac);
g_key_file_load_from_file(kf, CONFIG_INTERN, G_KEY_FILE_NONE, NULL);
g_assert_false(g_key_file_has_key(kf, group_by_name, "managed", NULL));
g_assert_true(g_key_file_has_key(kf, group_by_mac, "managed", NULL));
g_assert_cmpint(g_key_file_get_integer(kf, group_by_mac, "managed", NULL), ==, 0);
/* Resetting the managed state */
NMTST_EXPECT_NM_INFO("config: signal: *");
g_assert(nm_config_set_device_managed(config, dev, NM_TERNARY_DEFAULT, FALSE, NULL));
g_assert(nm_config_get_device_managed(config, dev, &managed, NULL, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_DEFAULT);
g_key_file_load_from_file(kf, CONFIG_INTERN, G_KEY_FILE_NONE, NULL);
g_assert_false(g_key_file_has_key(kf, group_by_name, "managed", NULL));
g_assert_false(g_key_file_has_key(kf, group_by_mac, "managed", NULL));
g_object_unref(config);
/* Both values set in the intern config file, different values */
g_key_file_set_string(kf, group_by_name, "managed", "1");
g_key_file_set_string(kf, group_by_mac, "managed", "0");
g_assert(g_key_file_save_to_file(kf, CONFIG_INTERN, NULL));
config = setup_config(NULL, CONFIG_USER, CONFIG_INTERN, NULL, "/no/such/dir", "", NULL);
g_assert(!nm_config_get_device_managed(config, dev, &managed, NULL, NULL));
g_object_unref(config);
/* Both values set in the intern config file, same values */
g_key_file_set_string(kf, group_by_name, "managed", "1");
g_key_file_set_string(kf, group_by_mac, "managed", "1");
g_assert(g_key_file_save_to_file(kf, CONFIG_INTERN, NULL));
config = setup_config(NULL, CONFIG_USER, CONFIG_INTERN, NULL, "/no/such/dir", "", NULL);
g_assert(nm_config_get_device_managed(config, dev, &managed, NULL, NULL));
g_assert_cmpint(managed, ==, NM_TERNARY_TRUE);
g_key_file_unref(kf);
g_object_unref(dev);
g_object_unref(config);
}
static void static void
test_config_global_dns(void) test_config_global_dns(void)
{ {
@ -1412,6 +1514,7 @@ main(int argc, char **argv)
g_test_add_func("/config/set-values", test_config_set_values); g_test_add_func("/config/set-values", test_config_set_values);
g_test_add_func("/config/global-dns", test_config_global_dns); g_test_add_func("/config/global-dns", test_config_global_dns);
g_test_add_func("/config/managed", test_config_managed);
g_test_add_func("/config/connectivity-check", test_config_connectivity_check); g_test_add_func("/config/connectivity-check", test_config_connectivity_check);
g_test_add_func("/config/signal", test_config_signal); g_test_add_func("/config/signal", test_config_signal);

View file

@ -7,6 +7,7 @@
#include <net/if.h> #include <net/if.h>
#include <byteswap.h> #include <byteswap.h>
#include <netinet/ip6.h>
/* need math.h for isinf() and INFINITY. No need to link with -lm */ /* need math.h for isinf() and INFINITY. No need to link with -lm */
#include <math.h> #include <math.h>
@ -2770,6 +2771,56 @@ test_nm_firewall_nft_stdio_mlag(void)
"nm-mlag-bond0\012delete table netdev nm-mlag-bond0\012"); "nm-mlag-bond0\012delete table netdev nm-mlag-bond0\012");
} }
static void
test_icmp6_checksum(void)
{
struct ip6_hdr ip6h = {};
guint8 *data;
guint16 c;
ip6h.ip6_src = NM_IN6ADDR_INIT(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
ip6h.ip6_dst = NM_IN6ADDR_INIT(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
data = (guint8[]) {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
c = nm_utils_icmp6_checksum(&ip6h.ip6_src, 12, data);
g_assert_cmpint(c, ==, htons(0xffb9));
ip6h.ip6_src = NM_IN6ADDR_INIT(0xfe,
0x80,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xc0,
0x60,
0x8c,
0xaf,
0xf6,
0x9b,
0xe4,
0x1a);
ip6h.ip6_dst = NM_IN6ADDR_INIT(0x20,
0x02,
0xaa,
0xaa,
0x00,
0x00,
0x00,
0x00,
0x64,
0xd4,
0x29,
0x32,
0x35,
0x85,
0x7c,
0x89);
data = (guint8[]) {0xdc, 0x74, 0x1a, 0xcc, 0xd3, 0x8e, 0xca, 0x34};
c = nm_utils_icmp6_checksum(&ip6h.ip6_src, 8, data);
g_assert_cmpint(c, ==, htons(0x39af));
}
/*****************************************************************************/ /*****************************************************************************/
NMTST_DEFINE(); NMTST_DEFINE();
@ -2848,5 +2899,7 @@ main(int argc, char **argv)
g_test_add_func("/core/test_nm_firewall_nft_stdio_mlag", test_nm_firewall_nft_stdio_mlag); g_test_add_func("/core/test_nm_firewall_nft_stdio_mlag", test_nm_firewall_nft_stdio_mlag);
g_test_add_func("/core/general/test_icmp6_checksum", test_icmp6_checksum);
return g_test_run(); return g_test_run();
} }

View file

@ -53,6 +53,44 @@ test_ip_reservation_shared4(void)
} }
} }
static void
test_ip_reservation_clat(void)
{
gs_unref_object NMPlatform *platform = NULL;
gs_unref_object NMNetns *netns = NULL;
NMNetnsIPReservation *res[8];
NMNetnsIPReservation *res1;
char buf[NM_INET_ADDRSTRLEN];
guint i;
platform = g_object_ref(NM_PLATFORM_GET);
netns = nm_netns_new(platform);
/* Allocate addresses 192.0.0.{5,6,7,0,1,2,3,4} */
for (i = 0; i < 8; i++) {
res[i] = nm_netns_ip_reservation_get(netns, NM_NETNS_IP_RESERVATION_TYPE_CLAT);
g_snprintf(buf, sizeof(buf), "192.0.0.%u", (i + 5) % 8);
nmtst_assert_ip4_address(res[i]->addr, buf);
g_assert_cmpint(res[i]->_ref_count, ==, 1);
}
/* Release an address and get it back */
nm_netns_ip_reservation_release(res[2]);
res[2] = nm_netns_ip_reservation_get(netns, NM_NETNS_IP_RESERVATION_TYPE_CLAT);
nmtst_assert_ip4_address(res[2]->addr, "192.0.0.7");
/* No reuse */
NMTST_EXPECT_NM_ERROR("netns[*]: clat: ran out of IP addresses");
res1 = nm_netns_ip_reservation_get(netns, NM_NETNS_IP_RESERVATION_TYPE_CLAT);
g_test_assert_expected_messages();
g_assert_null(res1);
/* Release all */
for (i = 0; i < 8; i++) {
nm_netns_ip_reservation_release(res[i]);
}
}
/*****************************************************************************/ /*****************************************************************************/
NMTST_DEFINE(); NMTST_DEFINE();
@ -64,6 +102,7 @@ main(int argc, char **argv)
nm_linux_platform_setup(); nm_linux_platform_setup();
g_test_add_func("/netns/ip_reservation/shared4", test_ip_reservation_shared4); g_test_add_func("/netns/ip_reservation/shared4", test_ip_reservation_shared4);
g_test_add_func("/netns/ip_reservation/clat", test_ip_reservation_clat);
return g_test_run(); return g_test_run();
} }

View file

@ -2135,16 +2135,20 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict
IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN
: NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN, : NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN,
"&s", "&s",
&v_str)) &v_str)) {
nm_l3_config_data_add_domain(l3cd, addr_family, v_str); nm_l3_config_data_add_domain(l3cd, addr_family, v_str);
nm_l3_config_data_add_search(l3cd, addr_family, v_str);
}
if (g_variant_lookup(dict, if (g_variant_lookup(dict,
IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS
: NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, : NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS,
"as", "as",
&var_iter)) { &var_iter)) {
while (g_variant_iter_next(var_iter, "&s", &v_str)) while (g_variant_iter_next(var_iter, "&s", &v_str)) {
nm_l3_config_data_add_domain(l3cd, addr_family, v_str); nm_l3_config_data_add_domain(l3cd, addr_family, v_str);
nm_l3_config_data_add_search(l3cd, addr_family, v_str);
}
g_variant_iter_free(var_iter); g_variant_iter_free(var_iter);
} }

View file

@ -60,16 +60,21 @@ nm_vpn_manager_activate_connection(NMVpnManager *manager, NMVpnConnection *vpn,
{ {
NMVpnManagerPrivate *priv; NMVpnManagerPrivate *priv;
NMVpnPluginInfo *plugin_info; NMVpnPluginInfo *plugin_info;
NMConnection *applied;
const char *service_name; const char *service_name;
NMDevice *device; NMDevice *device;
const char *user;
g_return_val_if_fail(NM_IS_VPN_MANAGER(manager), FALSE); g_return_val_if_fail(NM_IS_VPN_MANAGER(manager), FALSE);
g_return_val_if_fail(NM_IS_VPN_CONNECTION(vpn), FALSE); g_return_val_if_fail(NM_IS_VPN_CONNECTION(vpn), FALSE);
g_return_val_if_fail(!error || !*error, FALSE); g_return_val_if_fail(!error || !*error, FALSE);
priv = NM_VPN_MANAGER_GET_PRIVATE(manager); priv = NM_VPN_MANAGER_GET_PRIVATE(manager);
device = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(vpn)); device = nm_active_connection_get_device(NM_ACTIVE_CONNECTION(vpn));
g_assert(device); applied = nm_active_connection_get_applied_connection(NM_ACTIVE_CONNECTION(vpn));
nm_assert(device);
nm_assert(applied);
if (nm_device_get_state(device) != NM_DEVICE_STATE_ACTIVATED if (nm_device_get_state(device) != NM_DEVICE_STATE_ACTIVATED
&& nm_device_get_state(device) != NM_DEVICE_STATE_SECONDARIES) { && nm_device_get_state(device) != NM_DEVICE_STATE_SECONDARIES) {
g_set_error_literal(error, g_set_error_literal(error,
@ -101,6 +106,30 @@ nm_vpn_manager_activate_connection(NMVpnManager *manager, NMVpnConnection *vpn,
return FALSE; return FALSE;
} }
user = nm_utils_get_connection_first_permissions_user(applied);
if (user) {
NMSettingConnection *s_con;
s_con = nm_connection_get_setting_connection(applied);
nm_assert(s_con);
if (_nm_setting_connection_get_num_permissions_users(s_con) > 1) {
g_set_error_literal(error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
"private VPN connections with multiple users are not allowed.");
return FALSE;
}
if (!nm_vpn_plugin_info_supports_safe_private_file_access(plugin_info)) {
g_set_error(error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
"The '%s' plugin doesn't support private connections.",
nm_vpn_plugin_info_get_name(plugin_info));
return FALSE;
}
}
nm_vpn_connection_activate(vpn, plugin_info); nm_vpn_connection_activate(vpn, plugin_info);
if (!nm_vpn_plugin_info_supports_multiple(plugin_info)) { if (!nm_vpn_plugin_info_supports_multiple(plugin_info)) {

View file

@ -345,6 +345,7 @@ typedef enum {
NM_IP_CONFIG_SOURCE_VPN, NM_IP_CONFIG_SOURCE_VPN,
NM_IP_CONFIG_SOURCE_DHCP, NM_IP_CONFIG_SOURCE_DHCP,
NM_IP_CONFIG_SOURCE_NDISC, NM_IP_CONFIG_SOURCE_NDISC,
NM_IP_CONFIG_SOURCE_CLAT,
NM_IP_CONFIG_SOURCE_USER, NM_IP_CONFIG_SOURCE_USER,
} NMIPConfigSource; } NMIPConfigSource;

View file

@ -65,6 +65,7 @@
#define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier" #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_CHECK_CONNECTIVITY "check-connectivity"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS "sriov-num-vfs" #define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS "sriov-num-vfs"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION "keep-configuration" #define NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION "keep-configuration"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS "allowed-connections" #define NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS "allowed-connections"
@ -89,4 +90,7 @@
#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN \ #define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN \
NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN
#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE \
NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE
#endif /* __NM_CONFIG_BASE_H__ */ #endif /* __NM_CONFIG_BASE_H__ */

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