Compare commits

...

55 commits

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
94 changed files with 22003 additions and 2480 deletions

View file

@ -12,9 +12,9 @@ Please read
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/CONTRIBUTING.md
before opening the merge request. In particular, check that:
- [ ] the subject for all commits is concise and explicative
- [ ] the message for all commits explains the reason for the change
- [ ] the source is properly formatted
- [ ] any relevant documentation is up to date
- [ ] 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 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 source is properly formatted
- [ ] Any relevant documentation is up to date
- [ ] 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.

6
NEWS
View file

@ -47,6 +47,12 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
* 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

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
ProtectSystem=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=read-only
ProtectKernelLogs=true
ProtectSystem=true
RestrictRealtime=true
RestrictSUIDSGID=true
# We require file descriptors for DHCP etc. When activating many interfaces,
# the default limit of 1024 is easily reached.

View file

@ -183,6 +183,7 @@
<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.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.IPTunnel.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-ethtool.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-hostname.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-ethernet.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-infiniband.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.Dummy',
'org.freedesktop.NetworkManager.Device.Generic',
'org.freedesktop.NetworkManager.Device.Geneve',
'org.freedesktop.NetworkManager.Device.Hsr',
'org.freedesktop.NetworkManager.Device.IPTunnel',
'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
the keyfile.unmanaged-devices setting in NetworkManager.conf. Changes to
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"/>
@ -391,6 +394,20 @@
-->
<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:
@new_state: (<link linkend="NMDeviceState">NMDeviceState</link>) The new state of the device.

View file

@ -62,7 +62,7 @@
<!--
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.
Get the secrets belonging to this network configuration. Only secrets from

View file

@ -1436,15 +1436,31 @@
</arg>
<arg>
<option>managed</option>
<group>
<arg choice='plain'>--permanent</arg>
<arg choice='plain'>--permanent-only</arg>
</group>
<group choice='req'>
<arg choice='plain'>yes</arg>
<arg choice='plain'>no</arg>
<arg choice='plain'>up</arg>
<arg choice='plain'>down</arg>
<arg choice='plain'>reset</arg>
</group>
</arg>
</term>
<listitem>
<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>
</varlistentry>
@ -1852,9 +1868,9 @@
connections with an option of restoring the network configuration to a
known good state in case of an error.</para>
<para>If the a list of interface names is specified, the checkpoint is
taken, the checkpoint is takes only on the specified devices. Otherwise
a checkpoint is taken for all devices.</para>
<para>If a list of interface names is specified, the checkpoint is
taken only on the specified devices. Otherwise a checkpoint is taken for
all devices.</para>
<para>Currently the timeout defaults to 15 seconds. This may change in
a future version.</para>

View file

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

View file

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

View file

@ -5,12 +5,14 @@ src/core/NetworkManagerUtils.c
src/core/devices/adsl/nm-device-adsl.c
src/core/devices/bluetooth/nm-bluez-manager.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-bond.c
src/core/devices/nm-device-bridge.c
src/core/devices/nm-device-dummy.c
src/core/devices/nm-device-ethernet-utils.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-ip-tunnel.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-ethernet.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-infiniband.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-ethtool.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-hsr.c
src/libnm-core-impl/nm-setting-infiniband.c

1985
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

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

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_ethernet_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_infiniband_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 (!nm_inet_parse_bin(AF_INET, str, NULL, &props.local)
&& !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);
if (str) {
if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.group)
&& !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);

View file

@ -5962,7 +5962,6 @@ nm_device_get_route_metric_default(NMDeviceType device_type)
* in some aspects a VPN. */
case NM_DEVICE_TYPE_WIREGUARD:
return NM_VPN_ROUTE_METRIC_DEFAULT;
case NM_DEVICE_TYPE_ETHERNET:
case NM_DEVICE_TYPE_VETH:
return 100;
@ -5996,6 +5995,8 @@ nm_device_get_route_metric_default(NMDeviceType device_type)
return 470;
case NM_DEVICE_TYPE_VXLAN:
return 500;
case NM_DEVICE_TYPE_GENEVE:
return 525;
case NM_DEVICE_TYPE_DUMMY:
return 550;
case NM_DEVICE_TYPE_WIFI:
@ -13680,13 +13681,21 @@ activate_stage3_ip_config(NMDevice *self)
nm_device_get_ip_iface(self));
}
/* We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG above.
* Note that kernel changes the MTU of bond ports, so we want to commit the MTU
* afterwards!
/*
* Let's make sure MTU matches what is configured. The reason it's done at this
* precise location is twofold:
*
* This might reset the MTU to something different from the bond controller and
* it might not be a working configuration. But it's what the user asked for, so
* let's do it! */
* (1) Attaching ports above might affect the MTU.
*
* We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG
* above. This might reset the MTU to something different from the bond controller
* and it might not be a working configuration. But it's what the user asked for.
*
* (2) When MTU is under 1280 IPv6 can not work.
*
* Kernel will not expose sysctls, create or accept addresses that are needed for IPv6
* configuration when the MTU is too small (under 1280).
*/
_commit_mtu(self);
if (!nm_device_managed_type_is_external(self)
@ -13695,12 +13704,6 @@ activate_stage3_ip_config(NMDevice *self)
&& !NM_IN_STRSET(priv->ipv6_method,
NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
/* Ensure the MTU makes sense. If it was below 1280 the kernel would not
* expose any ipv6 sysctls or allow presence of any addresses on the interface,
* including LL, which * would make it impossible to autoconfigure MTU to a
* correct value. */
_commit_mtu(self);
/* Any method past this point requires an IPv6LL address. Use NM-controlled
* IPv6LL if this is not an assumed connection, since assumed connections
* will already have IPv6 set up.
@ -14937,6 +14940,241 @@ impl_device_get_applied_connection(NMDBusObject *obj,
/*****************************************************************************/
typedef struct {
NMDeviceManaged managed_state;
NMDeviceManagedFlags managed_flags;
} SetManagedData;
static gboolean
get_managed_match_by_mac(NMDevice *self, NMDeviceManagedFlags flags, gboolean *out, GError **error)
{
gboolean is_fake_hwaddr;
nm_assert(out);
if ((flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC)
&& (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME)) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"cannot match both by 'mac' and by 'interface-name'");
return FALSE;
}
nm_device_get_permanent_hw_address_full(self, TRUE, &is_fake_hwaddr);
if ((flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC) && is_fake_hwaddr) {
g_set_error_literal(
error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"cannot match by 'mac': the device doesn't have a permanent MAC address");
return FALSE;
}
if (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC)
*out = TRUE;
else if (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME)
*out = FALSE;
else
*out = !is_fake_hwaddr;
return TRUE;
}
/**
* set_managed:
* @self: the device
* @managed: the new managed state to set.
* @flags: flags to select different behaviors like storing to disk.
* @error: return location for a #GError, or %NULL
*
* Sets the managed state of the device. It can affect the runtime managed state
* if the %NM_DEVICE_MANAGED_FLAGS_RUNTIME is set, and to the value stored on disk
* (persistent across reboots) state if the %NM_DEVICE_MANAGED_FLAGS_PERMANENT is set.
*
* Returns: %TRUE if the managed state was set successfully, %FALSE otherwise.
*/
static gboolean
set_managed(NMDevice *self, NMDeviceManaged managed, NMDeviceManagedFlags flags, GError **error)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
nm_assert(
NM_IN_SET(managed, NM_DEVICE_MANAGED_NO, NM_DEVICE_MANAGED_YES, NM_DEVICE_MANAGED_RESET));
nm_assert((flags & ~NM_DEVICE_MANAGED_FLAGS_ALL) == 0);
if (!NM_FLAGS_ANY(flags, NM_DEVICE_MANAGED_FLAGS_PERMANENT | NM_DEVICE_MANAGED_FLAGS_RUNTIME)) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
_("set managed: no permanent or runtime was selected"));
return FALSE;
}
if (flags & NM_DEVICE_MANAGED_FLAGS_PERMANENT) {
NMTernary managed_to_disk, old = NM_TERNARY_DEFAULT;
gboolean by_mac;
managed_to_disk = managed == NM_DEVICE_MANAGED_RESET ? NM_TERNARY_DEFAULT : !!managed;
nm_config_get_device_managed(nm_manager_get_config(priv->manager), self, &old, NULL, error);
if (!get_managed_match_by_mac(self, flags, &by_mac, error))
return FALSE;
if (!nm_config_set_device_managed(nm_manager_get_config(priv->manager),
self,
managed_to_disk,
by_mac,
error))
return FALSE;
/* Update the unmanaged flags after the change on disk */
nm_device_set_unmanaged_by_user_conf(self);
if (managed_to_disk != NM_TERNARY_DEFAULT
&& managed_to_disk != !nm_device_get_unmanaged_flags(self, NM_UNMANAGED_USER_CONF)) {
/* We failed to make the new state effective on disk. Maybe the new config
* collides with other config. Try to revert and return error. Otherwise,
* we would set the runtime state correctly, but get an unexpected state
* after a reboot. */
nm_config_set_device_managed(nm_manager_get_config(priv->manager),
self,
old,
by_mac,
NULL);
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
_("failed to persist 'managed=%d' on disk, other configurations may be "
"overriding it"),
managed);
return FALSE;
}
}
if (flags & NM_DEVICE_MANAGED_FLAGS_RUNTIME) {
if (managed == NM_DEVICE_MANAGED_RESET) {
nm_device_set_unmanaged_by_flags(self,
NM_UNMANAGED_USER_EXPLICIT,
NM_UNMAN_FLAG_OP_FORGET,
NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT);
} else {
g_object_set(self, NM_DEVICE_MANAGED, !!managed, NULL);
/* If requested, set the administrative state of the device to UP if the
* new managed state is YES, and to DOWN if it's NO. */
if (flags & NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE) {
if (nm_device_get_ifindex(self))
nm_platform_link_change_flags(nm_device_get_platform(self),
nm_device_get_ifindex(self),
IFF_UP,
!!managed);
}
}
}
return TRUE;
}
static void
set_managed_cb(NMDevice *self,
GDBusMethodInvocation *context,
NMAuthSubject *subject,
GError *error,
gpointer user_data)
{
SetManagedData *set_managed_data = user_data;
NMDeviceManaged managed;
NMDeviceManagedFlags flags;
GError *local = NULL;
managed = set_managed_data->managed_state;
flags = set_managed_data->managed_flags;
nm_g_slice_free(set_managed_data);
if (!error) {
if (!NM_IN_SET(managed,
NM_DEVICE_MANAGED_NO,
NM_DEVICE_MANAGED_YES,
NM_DEVICE_MANAGED_RESET))
g_set_error_literal(&error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"Invalid managed value");
else if ((flags & ~NM_DEVICE_MANAGED_FLAGS_ALL) != 0)
g_set_error_literal(&error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"Invalid flags");
}
if (error) {
nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_MANAGED,
self,
FALSE,
NULL,
subject,
error->message);
g_dbus_method_invocation_return_gerror(context, error);
return;
}
if (!set_managed(self, managed, flags, &local)) {
nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_MANAGED,
self,
FALSE,
NULL,
subject,
local->message);
g_dbus_method_invocation_take_error(context, g_steal_pointer(&local));
return;
}
nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_MANAGED, self, TRUE, NULL, subject, NULL);
g_dbus_method_invocation_return_value(context, NULL);
}
static void
impl_device_set_managed(NMDBusObject *obj,
const NMDBusInterfaceInfoExtended *interface_info,
const NMDBusMethodInfoExtended *method_info,
GDBusConnection *connection,
const char *sender,
GDBusMethodInvocation *invocation,
GVariant *parameters)
{
NMDevice *self = NM_DEVICE(obj);
gs_free_error GError *error = NULL;
guint32 managed_u;
NMDeviceManaged managed;
guint32 flags_u;
NMDeviceManagedFlags flags;
SetManagedData *set_managed_data;
g_variant_get(parameters, "(uu)", &managed_u, &flags_u);
managed = managed_u;
flags = flags_u;
nm_assert(managed == managed_u && flags == flags_u);
set_managed_data = g_slice_new(SetManagedData);
*set_managed_data = (SetManagedData) {
.managed_state = managed,
.managed_flags = flags,
};
nm_device_auth_request(self,
invocation,
nm_device_get_applied_connection(self),
NM_AUTH_PERMISSION_NETWORK_CONTROL,
TRUE,
NULL,
set_managed_cb,
set_managed_data);
}
/*****************************************************************************/
static void
disconnect_cb(NMDevice *self,
GDBusMethodInvocation *context,
@ -19567,7 +19805,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
nm_assert(priv->type == NM_DEVICE_TYPE_UNKNOWN);
priv->type = g_value_get_uint(value);
nm_assert(priv->type > NM_DEVICE_TYPE_UNKNOWN);
nm_assert(priv->type <= NM_DEVICE_TYPE_IPVLAN);
nm_assert(priv->type <= NM_DEVICE_TYPE_GENEVE);
break;
case PROP_LINK_TYPE:
/* construct-only */
@ -19898,6 +20136,12 @@ static const NMDBusInterfaceInfoExtended interface_info_device = {
NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"),
NM_DEFINE_GDBUS_ARG_INFO("version_id", "t"), ), ),
.handle = impl_device_get_applied_connection, ),
NM_DEFINE_DBUS_METHOD_INFO_EXTENDED(
NM_DEFINE_GDBUS_METHOD_INFO_INIT("SetManaged",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS(
NM_DEFINE_GDBUS_ARG_INFO("managed", "u"),
NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ), ),
.handle = impl_device_set_managed, ),
NM_DEFINE_DBUS_METHOD_INFO_EXTENDED(NM_DEFINE_GDBUS_METHOD_INFO_INIT("Disconnect", ),
.handle = impl_device_disconnect, ),
NM_DEFINE_DBUS_METHOD_INFO_EXTENDED(NM_DEFINE_GDBUS_METHOD_INFO_INIT("Delete", ),

View file

@ -418,7 +418,6 @@ lease_parse_routes(NDhcp4ClientLease *lease,
in_addr_t gateway;
uint8_t plen;
guint32 m;
gboolean has_router_from_classless = FALSE;
gboolean has_classless = FALSE;
guint32 default_route_metric_offset = 0;
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.
* 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).
*/
for (i = 0; i < 2; i++) {
@ -460,8 +459,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
if (plen == 0) {
/* if there are multiple default routes, we add them with differing
* metrics. */
m = default_route_metric_offset++;
has_router_from_classless = TRUE;
m = default_route_metric_offset++;
} else
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);
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
* Static Routes option. */
continue;
@ -539,13 +537,10 @@ lease_parse_routes(NDhcp4ClientLease *lease,
continue;
}
if (has_router_from_classless) {
/* If the DHCP server returns both a Classless Static Routes option and a
* Router option, the DHCP client MUST ignore the Router option [RFC 3442].
*
* Be more lenient and ignore the Router option only if Classless Static
* Routes contain a default gateway (as other DHCP backends do).
*/
if (has_classless) {
/* RFC 3442: if the DHCP server returns both a Classless Static Routes
* option and a Router option, the DHCP client MUST ignore the Router
* option. */
continue;
}

View file

@ -298,12 +298,6 @@ main(int argc, char *argv[])
_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)
* based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */
config_cli = nm_config_cmd_line_options_new(
@ -328,6 +322,12 @@ main(int argc, char *argv[])
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_statedir();

View file

@ -111,6 +111,7 @@ libNetworkManager = static_library(
'devices/nm-device-ethernet-utils.c',
'devices/nm-device-factory.c',
'devices/nm-device-generic.c',
'devices/nm-device-geneve.c',
'devices/nm-device-hsr.c',
'devices/nm-device-infiniband.c',
'devices/nm-device-ip-tunnel.c',

View file

@ -39,7 +39,9 @@ typedef struct {
bool activation_lifetime_bound_to_profile_visibility : 1;
bool settings_connection_is_unsaved : 1;
bool settings_connection_is_shadowed_owned : 1;
bool permanent_managed_by_mac : 1;
NMUnmanFlagOp unmanaged_explicit;
NMTernary permanent_managed;
NMActivationReason activation_reason;
gulong dev_exported_change_id;
} DeviceCheckpoint;
@ -496,14 +498,19 @@ nm_checkpoint_rollback(NMCheckpoint *self)
/* Start rolling-back each device */
g_hash_table_iter_init(&iter, priv->devices);
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, "
"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,
(int) dev_checkpoint->state,
dev_checkpoint->realized,
dev_checkpoint->unmanaged_explicit,
dev_checkpoint->permanent_managed,
dev_checkpoint->settings_connection_is_unsaved,
!!dev_checkpoint->settings_connection_shadowed,
dev_checkpoint->settings_connection_is_shadowed_owned);
@ -541,6 +548,43 @@ nm_checkpoint_rollback(NMCheckpoint *self)
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 (nm_device_get_state(device) != NM_DEVICE_STATE_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;
const char *path;
NMActRequest *act_request;
gboolean perm_managed_by_mac;
gs_free_error GError *error = NULL;
nm_assert(NM_IS_DEVICE(device));
nm_assert(nm_device_is_real(device));
@ -728,12 +774,26 @@ device_checkpoint_create(NMCheckpoint *self, NMDevice *device)
} else
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);
if (act_request) {
NMSettingsStorage *storage;
gboolean shadowed_owned = FALSE;
const char *shadowed_file;
gs_free_error GError *error = NULL;
NMSettingsStorage *storage;
gboolean shadowed_owned = FALSE;
const char *shadowed_file;
settings_connection = nm_act_request_get_settings_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",
nm_device_get_iface(device),
error->message);
g_clear_error(&error);
}
}
}

View file

@ -2058,12 +2058,15 @@ _match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
{
char **groups;
gsize i, j, ngroups;
char *connection_tag = NULL;
char *main_group = NULL;
MatchSectionInfo *match_section_infos = NULL;
const char *prefix;
const char *prefix, *prefix_intern;
prefix =
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.
*
@ -2074,27 +2077,36 @@ _match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
if (!groups)
return NULL;
if (ngroups > 0) {
gsize l = strlen(prefix);
for (i = 0, j = 0; i < ngroups; i++) {
if (g_str_has_prefix(groups[i], prefix)) {
if (groups[i][l] == '\0')
connection_tag = groups[i];
else
groups[j++] = groups[i];
} else
g_free(groups[i]);
for (i = 0, j = 0; i < ngroups; i++) {
if (nm_streq0(groups[i], prefix)) {
main_group = groups[i];
} else if (nm_streq0(groups[i], prefix_intern)) {
/* [.intern.connection] and [.intern.device] should not exist */
_nm_log(LOGL_WARN,
LOGD_CORE,
0,
NULL,
NULL,
"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);
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;
for (i = 0; i < ngroups; i++) {
/* pass ownership of @group on... */
@ -2103,9 +2115,9 @@ _match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
groups[ngroups - i - 1],
is_device);
}
if (connection_tag) {
/* pass ownership of @connection_tag on... */
_match_section_info_init(&match_section_infos[i], keyfile, connection_tag, is_device);
if (main_group) {
/* pass ownership of @main_group on... */
_match_section_info_init(&match_section_infos[i], keyfile, main_group, is_device);
}
g_free(groups);

View file

@ -2076,6 +2076,210 @@ nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled)
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:
* @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);
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 ... */
extern guint _nm_config_match_nm_version;
extern char *_nm_config_match_env;

View file

@ -56,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);
/*****************************************************************************/
@ -77,7 +77,7 @@ static void
_notify_platform_handle(NMIPConfig *self, gint64 now_msec)
{
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);
@ -98,7 +98,7 @@ _notify_platform_cb(gpointer user_data)
}
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 IS_IPv4 = NM_IS_IPv4(addr_family);
@ -953,7 +953,7 @@ _handle_l3cd_changed(NMIPConfig *self, const NML3ConfigData *l3cd)
}
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 IS_IPv4 = NM_IS_IPv4(addr_family);

View file

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

View file

@ -3208,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
* identifier of a route, see nm_platform_ip4_route_cmp()).
*
* Note however that for ECMP routes we currently can only merge routes
* that agree in their onlink flag. So a route without gateway cannot
* merge with an onlink route that has a gateway. That needs fixing,
* 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). */
* The onlink flag is tracked per-nexthop (in NMPlatformIP4RtNextHop.rtnh_flags
* for extra nexthops, and in r_rtm_flags for the first nexthop). ECMP routes
* can be merged regardless of per-nexthop onlink flags. */
r.r4.r_rtm_flags &= ~((unsigned) RTNH_F_ONLINK);
}

View file

@ -657,7 +657,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE:
nm_strbuf_append(&s,
&l,
", obj-type-flags=0x%x",
", obj-type-flags=0x%" G_GINT64_MODIFIER "x",
notify_data->platform_change_on_idle.obj_type_flags);
break;
case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT:
@ -1604,7 +1604,7 @@ _load_link(NML3Cfg *self, gboolean initial)
/*****************************************************************************/
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;

View file

@ -178,7 +178,7 @@ typedef struct {
} platform_change;
struct {
guint32 obj_type_flags;
guint64 obj_type_flags;
} platform_change_on_idle;
struct {
@ -207,7 +207,7 @@ struct _NML3Cfg {
* NML3Cfg instance. We track some per-l3cfg-data that is only
* relevant to NMNetns here. */
struct {
guint32 signal_pending_obj_type_flags;
guint64 signal_pending_obj_type_flags;
CList signal_pending_lst;
CList ecmp_track_ifindex_lst_head;
} internal_netns;
@ -223,7 +223,7 @@ NML3Cfg *nm_l3cfg_new(NMNetns *netns, int ifindex);
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,
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_b, route_a, weight);
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(
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];
*nh = (NMPlatformIP4RtNextHop) {
.ifindex = r->ifindex,
.gateway = r->gateway,
.weight = r->weight,
.ifindex = r->ifindex,
.gateway = r->gateway,
.weight = r->weight,
.rtnh_flags = r->r_rtm_flags & RTNH_F_ONLINK,
};
}
i++;

View file

@ -1230,7 +1230,7 @@ out:
}
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[] = {
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++) {
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 *arr2 = NULL;
NMPLookup lookup;
@ -1408,7 +1408,7 @@ nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolea
}
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)) {
/* 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;
}
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 *
nmtstp_link_gre_add(NMPlatform *platform,
int external_command,
@ -3024,8 +3079,6 @@ nmtstp_link_vxlan_add(NMPlatform *platform,
return pllink;
}
/*****************************************************************************/
const NMPlatformLink *
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
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 NMPlatformLink *
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,
int external_command,
const char *name,

View file

@ -1388,10 +1388,11 @@ test_software_detect(gconstpointer user_data)
const gboolean ext = test_data->external_command;
NMPlatformLnkBridge lnk_bridge = {};
NMPlatformLnkTun lnk_tun;
NMPlatformLnkGre lnk_gre = {};
NMPlatformLnkVti lnk_vti = {};
NMPlatformLnkVti6 lnk_vti6 = {};
nm_auto_close int tun_fd = -1;
NMPlatformLnkGeneve lnk_geneve = {};
NMPlatformLnkGre lnk_gre = {};
NMPlatformLnkVti lnk_vti = {};
NMPlatformLnkVti6 lnk_vti6 = {};
nm_auto_close int tun_fd = -1;
gboolean module_loaded;
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");
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:
module_loaded = nmtstp_ensure_module("ip_gre");
@ -2208,6 +2234,34 @@ test_software_detect(gconstpointer user_data)
}
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:
{
const NMPlatformLnkWireGuard *plnk = &lnk->lnk_wireguard;
@ -4143,6 +4197,8 @@ _nmtstp_setup_tests(void)
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/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/gretap", NM_LINK_TYPE_GRETAP, 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;
@ -2480,6 +2552,8 @@ _nmtstp_setup_tests(void)
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_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/ip4_metric0", test_ip4_route_metric0);
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);
}
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
_con_get_request_start(Request *req)
{
@ -1103,7 +1136,7 @@ _con_get_request_start(Request *req)
* unprivileged users.
*/
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_ANY_SYSTEM_SECRET_FLAGS,
NULL))) {

View file

@ -781,7 +781,8 @@ validate_secret_flags(NMConnection *connection, GVariant *secrets, ForEachSecret
static gboolean
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

View file

@ -311,6 +311,108 @@ test_config_override(void)
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
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/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/signal", test_config_signal);

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
: NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN,
"&s",
&v_str))
&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,
IS_IPv4 ? NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS
: NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS,
"as",
&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_search(l3cd, addr_family, v_str);
}
g_variant_iter_free(var_iter);
}

View file

@ -90,4 +90,7 @@
#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_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__ */

View file

@ -2106,8 +2106,29 @@ global:
libnm_1_58_0 {
global:
nm_connection_get_setting_geneve;
nm_device_geneve_get_df;
nm_device_geneve_get_dst_port;
nm_device_geneve_get_id;
nm_device_geneve_get_remote;
nm_device_geneve_get_tos;
nm_device_geneve_get_ttl;
nm_device_geneve_get_type;
nm_device_managed_flags_get_type;
nm_device_managed_get_type;
nm_device_set_managed_async;
nm_device_set_managed_finish;
nm_ip_config_get_clat_address;
nm_ip_config_get_clat_pref64;
nm_setting_geneve_df_get_type;
nm_setting_geneve_get_destination_port;
nm_setting_geneve_get_df;
nm_setting_geneve_get_id;
nm_setting_geneve_get_remote;
nm_setting_geneve_get_tos;
nm_setting_geneve_get_ttl;
nm_setting_geneve_get_type;
nm_setting_geneve_new;
nm_setting_ip4_config_clat_get_type;
nm_setting_ip4_config_get_clat;
nm_utils_wifi_6ghz_freqs;

View file

@ -16,6 +16,7 @@ libnm_client_impl_sources = files(
'nm-device-bt.c',
'nm-device-dummy.c',
'nm-device-ethernet.c',
'nm-device-geneve.c',
'nm-device-generic.c',
'nm-device-hsr.c',
'nm-device-infiniband.c',

View file

@ -29,6 +29,7 @@
#include "nm-device-dummy.h"
#include "nm-device-ethernet.h"
#include "nm-device-generic.h"
#include "nm-device-geneve.h"
#include "nm-device-hsr.h"
#include "nm-device-infiniband.h"
#include "nm-device-ip-tunnel.h"

View file

@ -0,0 +1,344 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2026 Red Hat, Inc.
*/
#include "libnm-client-impl/nm-default-libnm.h"
#include "nm-device-geneve.h"
#include "nm-setting-connection.h"
#include "nm-setting-geneve.h"
#include "nm-utils.h"
#include "nm-object-private.h"
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_ID,
PROP_REMOTE,
PROP_TOS,
PROP_TTL,
PROP_DST_PORT,
PROP_DF, );
typedef struct {
char *remote;
guint32 id;
gint32 ttl;
guint16 dst_port;
guint8 df;
guint8 tos;
} 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, NMObject, NMDevice)
/*****************************************************************************/
/**
* nm_device_geneve_get_id:
* @device: a #NMDeviceGeneve
*
* Returns: the device's GENEVE ID.
*
* Since: 1.58
**/
guint
nm_device_geneve_get_id(NMDeviceGeneve *device)
{
g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
return NM_DEVICE_GENEVE_GET_PRIVATE(device)->id;
}
/**
* nm_device_geneve_get_remote:
* @device: a #NMDeviceGeneve
*
* Returns: the IP address of the remote tunnel endpoint
*
* Since: 1.58
**/
const char *
nm_device_geneve_get_remote(NMDeviceGeneve *device)
{
g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), NULL);
return _nml_coerce_property_str_not_empty(NM_DEVICE_GENEVE_GET_PRIVATE(device)->remote);
}
/**
* nm_device_geneve_get_dst_port:
* @device: a #NMDeviceGeneve
*
* Returns: the UDP destination port
*
* Since: 1.58
**/
guint
nm_device_geneve_get_dst_port(NMDeviceGeneve *device)
{
g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
return NM_DEVICE_GENEVE_GET_PRIVATE(device)->dst_port;
}
/**
* nm_device_geneve_get_tos:
* @device: a #NMDeviceGeneve
*
* Returns: the TOS value to use in outgoing packets
*
* Since: 1.58
**/
guint
nm_device_geneve_get_tos(NMDeviceGeneve *device)
{
g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
return NM_DEVICE_GENEVE_GET_PRIVATE(device)->tos;
}
/**
* nm_device_geneve_get_ttl:
* @device: a #NMDeviceGeneve
*
* Returns: the time-to-live value to use in outgoing packets
*
* Since: 1.58
**/
guint
nm_device_geneve_get_ttl(NMDeviceGeneve *device)
{
g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
return NM_DEVICE_GENEVE_GET_PRIVATE(device)->ttl;
}
/**
* nm_device_geneve_get_df:
* @device: a #NMDeviceGeneve
*
* Returns: the Don't Fragment (DF) bit to set in outgoing packets
*
* Since: 1.58
**/
guint
nm_device_geneve_get_df(NMDeviceGeneve *device)
{
g_return_val_if_fail(NM_IS_DEVICE_GENEVE(device), 0);
return NM_DEVICE_GENEVE_GET_PRIVATE(device)->df;
}
static gboolean
connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
{
NMSettingGeneve *s_geneve;
if (!NM_DEVICE_CLASS(nm_device_geneve_parent_class)
->connection_compatible(device, connection, error))
return FALSE;
if (!nm_connection_is_type(connection, NM_SETTING_GENEVE_SETTING_NAME)) {
g_set_error_literal(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
_("The connection was not a GENEVE connection."));
return FALSE;
}
s_geneve = nm_connection_get_setting_geneve(connection);
if (nm_setting_geneve_get_id(s_geneve) != nm_device_geneve_get_id(NM_DEVICE_GENEVE(device))) {
g_set_error_literal(
error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
_("The GENEVE identifiers of the device and the connection didn't match."));
return FALSE;
}
return TRUE;
}
static GType
get_setting_type(NMDevice *device)
{
return NM_TYPE_SETTING_GENEVE;
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMDeviceGeneve *device = NM_DEVICE_GENEVE(object);
switch (prop_id) {
case PROP_ID:
g_value_set_uint(value, nm_device_geneve_get_id(device));
break;
case PROP_REMOTE:
g_value_set_string(value, nm_device_geneve_get_remote(device));
break;
case PROP_TOS:
g_value_set_uint(value, nm_device_geneve_get_tos(device));
break;
case PROP_TTL:
g_value_set_int(value, nm_device_geneve_get_ttl(device));
break;
case PROP_DST_PORT:
g_value_set_uint(value, nm_device_geneve_get_dst_port(device));
break;
case PROP_DF:
g_value_set_uint(value, nm_device_geneve_get_df(device));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
nm_device_geneve_init(NMDeviceGeneve *device)
{}
static void
finalize(GObject *object)
{
NMDeviceGenevePrivate *priv = NM_DEVICE_GENEVE_GET_PRIVATE(object);
g_free(priv->remote);
G_OBJECT_CLASS(nm_device_geneve_parent_class)->finalize(object);
}
const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_geneve = NML_DBUS_META_IFACE_INIT_PROP(
NM_DBUS_INTERFACE_DEVICE_GENEVE,
nm_device_geneve_get_type,
NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30,
NML_DBUS_META_IFACE_DBUS_PROPERTIES(
NML_DBUS_META_PROPERTY_INIT_Y("Df", PROP_DF, NMDeviceGeneve, _priv.df),
NML_DBUS_META_PROPERTY_INIT_Q("DstPort", PROP_DST_PORT, NMDeviceGeneve, _priv.dst_port),
NML_DBUS_META_PROPERTY_INIT_U("Id", PROP_ID, NMDeviceGeneve, _priv.id),
NML_DBUS_META_PROPERTY_INIT_S("Remote", PROP_REMOTE, NMDeviceGeneve, _priv.remote),
NML_DBUS_META_PROPERTY_INIT_Y("Tos", PROP_TOS, NMDeviceGeneve, _priv.tos),
NML_DBUS_META_PROPERTY_INIT_I("Ttl", PROP_TTL, NMDeviceGeneve, _priv.ttl), ), );
static void
nm_device_geneve_class_init(NMDeviceGeneveClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
NMObjectClass *nm_object_class = NM_OBJECT_CLASS(klass);
NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
object_class->get_property = get_property;
object_class->finalize = finalize;
_NM_OBJECT_CLASS_INIT_PRIV_PTR_DIRECT(nm_object_class, NMDeviceGeneve);
device_class->connection_compatible = connection_compatible;
device_class->get_setting_type = get_setting_type;
/**
* NMDeviceGeneve:id:
*
* The device's GENEVE ID.
*
* Since: 1.58
**/
obj_properties[PROP_ID] = g_param_spec_uint(NM_DEVICE_GENEVE_ID,
"",
"",
0,
(1 << 24) - 1,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMDeviceGeneve:remote:
*
* The IP address of the remote tunnel endpoint.
*
* Since: 1.58
*/
obj_properties[PROP_REMOTE] = g_param_spec_string(NM_DEVICE_GENEVE_REMOTE,
"",
"",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMDeviceGeneve:tos:
*
* The TOS value to use in outgoing packets.
*
* Since: 1.58
*/
obj_properties[PROP_TOS] = g_param_spec_uchar(NM_DEVICE_GENEVE_TOS,
"",
"",
0,
255,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMDeviceGeneve:ttl:
*
* The time-to-live value to use in outgoing packets.
*
* Since: 1.58
*/
obj_properties[PROP_TTL] = g_param_spec_int(NM_DEVICE_GENEVE_TTL,
"",
"",
-1,
255,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMDeviceGeneve:dst-port:
*
* The UDP destination port used to communicate with the remote GENEVE tunnel
* endpoint.
*
* Since: 1.58
*/
obj_properties[PROP_DST_PORT] = g_param_spec_uint(NM_DEVICE_GENEVE_DST_PORT,
"",
"",
0,
65535,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMDeviceGeneve:df:
*
* The Don't Fragment (DF) bit to set in outgoing packets.
*
* Since: 1.58
*/
obj_properties[PROP_DF] = g_param_spec_uchar(NM_DEVICE_GENEVE_DF,
"",
"",
0,
2,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
_nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_device_geneve);
}

View file

@ -302,6 +302,7 @@ coerce_type(NMDeviceType type)
case NM_DEVICE_TYPE_TUN:
case NM_DEVICE_TYPE_VETH:
case NM_DEVICE_TYPE_GENERIC:
case NM_DEVICE_TYPE_GENEVE:
case NM_DEVICE_TYPE_UNUSED1:
case NM_DEVICE_TYPE_UNUSED2:
case NM_DEVICE_TYPE_UNKNOWN:
@ -1445,9 +1446,7 @@ nm_device_get_managed(NMDevice *device)
*
* Since: 1.2
*
* Deprecated: 1.22: Use the async command nm_client_dbus_set_property() on
* nm_object_get_path(), interface %NM_DBUS_INTERFACE_DEVICE to set the
* "Managed" property to a "(b)" boolean value.
* Deprecated: 1.22: Use nm_device_set_managed_async() instead.
* This function is deprecated because it calls a synchronous D-Bus method
* and modifies the content of the NMClient cache client side. Also, it does
* not emit a property changed signal.
@ -1469,6 +1468,72 @@ nm_device_set_managed(NMDevice *device, gboolean managed)
managed);
}
/**
* nm_device_set_managed_async:
* @device: a #NMDevice
* @managed: the managed state
* @flags: the flags argument. See #NMDeviceManagedFlags.
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to be called when the set_managed operation completes
* @user_data: caller-specific data passed to @callback
*
* Asynchronously begins setting the managed state of the device. With the flags
* argument different behaviors can be achieved, such as persisting the state to
* disk or matching the device by MAC address.
*
* Since: 1.58
**/
void
nm_device_set_managed_async(NMDevice *device,
NMDeviceManaged managed,
NMDeviceManagedFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail(NM_IS_DEVICE(device));
g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable));
_nm_client_dbus_call(_nm_object_get_client(device),
device,
nm_device_set_managed_async,
cancellable,
callback,
user_data,
_nm_object_get_path(device),
NM_DBUS_INTERFACE_DEVICE,
"SetManaged",
g_variant_new("(uu)", managed, flags),
G_VARIANT_TYPE("()"),
G_DBUS_CALL_FLAGS_NONE,
NM_DBUS_DEFAULT_TIMEOUT_MSEC,
nm_dbus_connection_call_finish_void_strip_dbus_error_cb);
}
/**
* nm_device_set_managed_finish:
* @device: a #NMDevice
* @result: the result passed to the #GAsyncReadyCallback
* @error: location for a #GError, or %NULL
*
* Gets the result of a call to nm_device_set_managed_async().
*
* Returns: %TRUE on success, %FALSE on error, in which case @error
* will be set.
*
* Since: 1.58
**/
gboolean
nm_device_set_managed_finish(NMDevice *device, GAsyncResult *result, GError **error)
{
g_return_val_if_fail(NM_IS_DEVICE(device), FALSE);
g_return_val_if_fail(nm_g_task_is_valid(result, device, nm_device_set_managed_async), FALSE);
return g_task_propagate_boolean(G_TASK(result), error);
}
/*****************************************************************************/
/**
* nm_device_get_autoconnect:
* @device: a #NMDevice
@ -1792,6 +1857,8 @@ get_type_name(NMDevice *device)
return _("MACVLAN");
case NM_DEVICE_TYPE_VXLAN:
return _("VXLAN");
case NM_DEVICE_TYPE_GENEVE:
return _("GENEVE");
case NM_DEVICE_TYPE_IP_TUNNEL:
return _("IPTunnel");
case NM_DEVICE_TYPE_TUN:

View file

@ -789,6 +789,7 @@ const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = {
&_nml_dbus_meta_iface_nm_device_bridge,
&_nml_dbus_meta_iface_nm_device_dummy,
&_nml_dbus_meta_iface_nm_device_generic,
&_nml_dbus_meta_iface_nm_device_geneve,
&_nml_dbus_meta_iface_nm_device_hsr,
&_nml_dbus_meta_iface_nm_device_iptunnel,
&_nml_dbus_meta_iface_nm_device_infiniband,

View file

@ -579,7 +579,7 @@ struct _NMLDBusMetaIface {
NML_DBUS_META_IFACE_OBJ_PROPERTIES(), \
##__VA_ARGS__)
extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[47];
extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[48];
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint;
@ -593,6 +593,7 @@ extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bond;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_bridge;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_dummy;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_generic;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_geneve;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_hsr;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_infiniband;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel;

View file

@ -37,6 +37,7 @@
#include "nm-setting-dummy.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-generic.h"
#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hostname.h"
#include "nm-setting-hsr.h"
@ -114,6 +115,7 @@
#include "nm-device-dummy.h"
#include "nm-device-ethernet.h"
#include "nm-device-generic.h"
#include "nm-device-geneve.h"
#include "nm-device-hsr.h"
#include "nm-device-infiniband.h"
#include "nm-device-ip-tunnel.h"

View file

@ -18,6 +18,7 @@ libnm_client_headers = files(
'nm-device-dummy.h',
'nm-device-ethernet.h',
'nm-device-generic.h',
'nm-device-geneve.h',
'nm-device-hsr.h',
'nm-device-infiniband.h',
'nm-device-ip-tunnel.h',

View file

@ -41,6 +41,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceBt, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceDummy, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceEthernet, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneric, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneve, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceHsr, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceIPTunnel, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceInfiniband, g_object_unref)
@ -80,6 +81,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingDcb, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingDummy, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingEthtool, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingGeneric, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingGeneve, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingGsm, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingHostname, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingHsr, g_object_unref)

View file

@ -0,0 +1,60 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2015 Red Hat, Inc.
*/
#ifndef __NM_DEVICE_GENEVE_H__
#define __NM_DEVICE_GENEVE_H__
#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION)
#error "Only <NetworkManager.h> can be included directly."
#endif
#include "nm-device.h"
G_BEGIN_DECLS
#define NM_TYPE_DEVICE_GENEVE (nm_device_geneve_get_type())
#define NM_DEVICE_GENEVE(obj) \
(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_DST_PORT "dst-port"
#define NM_DEVICE_GENEVE_DF "df"
/**
* NMDeviceGeneve:
*
* Since: 1.58
*/
typedef struct _NMDeviceGeneve NMDeviceGeneve;
typedef struct _NMDeviceGeneveClass NMDeviceGeneveClass;
NM_AVAILABLE_IN_1_58
GType nm_device_geneve_get_type(void);
NM_AVAILABLE_IN_1_58
guint nm_device_geneve_get_id(NMDeviceGeneve *device);
NM_AVAILABLE_IN_1_58
const char *nm_device_geneve_get_remote(NMDeviceGeneve *device);
NM_AVAILABLE_IN_1_58
guint nm_device_geneve_get_dst_port(NMDeviceGeneve *device);
NM_AVAILABLE_IN_1_58
guint nm_device_geneve_get_tos(NMDeviceGeneve *device);
NM_AVAILABLE_IN_1_58
guint nm_device_geneve_get_ttl(NMDeviceGeneve *device);
NM_AVAILABLE_IN_1_58
guint nm_device_geneve_get_df(NMDeviceGeneve *device);
G_END_DECLS
#endif /* __NM_DEVICE_GENEVE_H__ */

View file

@ -150,6 +150,15 @@ NM_AVAILABLE_IN_1_2
NM_DEPRECATED_IN_1_22
_NM_DEPRECATED_SYNC_METHOD
void nm_device_set_managed(NMDevice *device, gboolean managed);
NM_AVAILABLE_IN_1_58
void nm_device_set_managed_async(NMDevice *device,
NMDeviceManaged managed,
NMDeviceManagedFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
NM_AVAILABLE_IN_1_58
gboolean nm_device_set_managed_finish(NMDevice *device, GAsyncResult *result, GError **error);
gboolean nm_device_get_autoconnect(NMDevice *device);

View file

@ -1378,6 +1378,34 @@
gprop-type="gchararray"
/>
</setting>
<setting name="geneve"
gtype="NMSettingGeneve"
>
<property name="destination-port"
dbus-type="u"
gprop-type="guint"
/>
<property name="df"
dbus-type="i"
gprop-type="gint"
/>
<property name="id"
dbus-type="u"
gprop-type="guint"
/>
<property name="remote"
dbus-type="s"
gprop-type="gchararray"
/>
<property name="tos"
dbus-type="u"
gprop-type="guint"
/>
<property name="ttl"
dbus-type="i"
gprop-type="gint"
/>
</setting>
<setting name="gsm"
gtype="NMSettingGsm"
>

View file

@ -17,6 +17,7 @@ libnm_core_settings_sources = files(
'nm-setting-dummy.c',
'nm-setting-ethtool.c',
'nm-setting-generic.c',
'nm-setting-geneve.c',
'nm-setting-gsm.c',
'nm-setting-hostname.c',
'nm-setting-hsr.c',

View file

@ -3272,6 +3272,7 @@ nm_connection_is_virtual(NMConnection *connection)
NM_SETTING_BOND_SETTING_NAME,
NM_SETTING_BRIDGE_SETTING_NAME,
NM_SETTING_DUMMY_SETTING_NAME,
NM_SETTING_GENEVE_SETTING_NAME,
NM_SETTING_HSR_SETTING_NAME,
NM_SETTING_IP_TUNNEL_SETTING_NAME,
NM_SETTING_IPVLAN_SETTING_NAME,
@ -3526,6 +3527,22 @@ nm_connection_get_setting_generic(NMConnection *connection)
return _nm_connection_get_setting_by_metatype(connection, NM_META_SETTING_TYPE_GENERIC);
}
/**
* nm_connection_get_setting_geneve:
* @connection: the #NMConnection
*
* A shortcut to return any #NMSettingGeneve the connection might contain.
*
* Returns: (transfer none): an #NMSettingGeneve if the connection contains one, otherwise NULL
*
* Since: 1.58
**/
NMSettingGeneve *
nm_connection_get_setting_geneve(NMConnection *connection)
{
return _nm_connection_get_setting_by_metatype(connection, NM_META_SETTING_TYPE_GENEVE);
}
/**
* nm_connection_get_setting_gsm:
* @connection: the #NMConnection

View file

@ -27,6 +27,7 @@
#include "nm-setting-dummy.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-generic.h"
#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hostname.h"
#include "nm-setting-hsr.h"
@ -324,6 +325,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_GENERIC_SETTING_NAME,
.get_setting_gtype = nm_setting_generic_get_type,
},
[NM_META_SETTING_TYPE_GENEVE] =
{
.meta_type = NM_META_SETTING_TYPE_GENEVE,
.setting_priority = NM_SETTING_PRIORITY_HW_BASE,
.setting_name = NM_SETTING_GENEVE_SETTING_NAME,
.get_setting_gtype = nm_setting_geneve_get_type,
},
[NM_META_SETTING_TYPE_GSM] =
{
.meta_type = NM_META_SETTING_TYPE_GSM,
@ -655,6 +663,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
NM_META_SETTING_TYPE_CDMA,
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_GENERIC,
NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HSR,
NM_META_SETTING_TYPE_INFINIBAND,

View file

@ -0,0 +1,354 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2026 Red Hat, Inc.
*/
#include "libnm-core-impl/nm-default-libnm-core.h"
#include "nm-setting-geneve.h"
#include <stdlib.h>
#include "nm-utils.h"
#include "nm-setting-private.h"
/**
* SECTION:nm-setting-geneve
* @short_description: Describes connection properties for GENEVE interfaces
*
* The #NMSettingGeneve object is a #NMSetting subclass that describes properties
* necessary for connection to GENEVE interfaces.
**/
#define DST_PORT_DEFAULT 6081
NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_ID,
PROP_REMOTE,
PROP_DESTINATION_PORT,
PROP_TOS,
PROP_TTL,
PROP_DF, );
typedef struct {
char *remote;
guint32 id;
guint32 destination_port;
guint32 tos;
gint32 ttl;
int df;
} NMSettingGenevePrivate;
/**
* NMSettingGeneve:
*
* GENEVE Settings
*/
struct _NMSettingGeneve {
NMSetting parent;
NMSettingGenevePrivate _priv;
};
struct _NMSettingGeneveClass {
NMSettingClass parent;
};
G_DEFINE_TYPE(NMSettingGeneve, nm_setting_geneve, NM_TYPE_SETTING)
#define NM_SETTING_GENEVE_GET_PRIVATE(o) \
_NM_GET_PRIVATE(o, NMSettingGeneve, NM_IS_SETTING_GENEVE, NMSetting)
/*****************************************************************************/
/**
* nm_setting_geneve_get_id:
* @setting: the #NMSettingGeneve
*
* Returns: the #NMSettingGeneve:id property of the setting
*
* Since: 1.58
**/
guint
nm_setting_geneve_get_id(NMSettingGeneve *setting)
{
g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), 0);
return NM_SETTING_GENEVE_GET_PRIVATE(setting)->id;
}
/**
* nm_setting_geneve_get_remote:
* @setting: the #NMSettingGeneve
*
* Returns: the #NMSettingGeneve:remote property of the setting
*
* Since: 1.58
**/
const char *
nm_setting_geneve_get_remote(NMSettingGeneve *setting)
{
g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), NULL);
return NM_SETTING_GENEVE_GET_PRIVATE(setting)->remote;
}
/**
* nm_setting_geneve_get_destination_port:
* @setting: the #NMSettingGeneve
*
* Returns: the #NMSettingGeneve:destination-port property of the setting
*
* Since: 1.58
**/
guint
nm_setting_geneve_get_destination_port(NMSettingGeneve *setting)
{
g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), DST_PORT_DEFAULT);
return NM_SETTING_GENEVE_GET_PRIVATE(setting)->destination_port;
}
/**
* nm_setting_geneve_get_tos:
* @setting: the #NMSettingGeneve
*
* Returns: the #NMSettingGeneve:tos property of the setting
*
* Since: 1.58
**/
guint
nm_setting_geneve_get_tos(NMSettingGeneve *setting)
{
g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), 0);
return NM_SETTING_GENEVE_GET_PRIVATE(setting)->tos;
}
/**
* nm_setting_geneve_get_ttl:
* @setting: the #NMSettingGeneve
*
* Returns: the #NMSettingGeneve:ttl property of the setting
*
* Since: 1.58
**/
guint
nm_setting_geneve_get_ttl(NMSettingGeneve *setting)
{
g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), 0);
return NM_SETTING_GENEVE_GET_PRIVATE(setting)->ttl;
}
/**
* nm_setting_geneve_get_df:
* @setting: the #NMSettingGeneve
*
* Returns: the #NMSettingGeneve:df property of the setting
*
* Since: 1.58
**/
NMSettingGeneveDf
nm_setting_geneve_get_df(NMSettingGeneve *setting)
{
g_return_val_if_fail(NM_IS_SETTING_GENEVE(setting), NM_SETTING_GENEVE_DF_UNSET);
return NM_SETTING_GENEVE_GET_PRIVATE(setting)->df;
}
/*****************************************************************************/
static gboolean
verify(NMSetting *setting, NMConnection *connection, GError **error)
{
NMSettingGenevePrivate *priv = NM_SETTING_GENEVE_GET_PRIVATE(setting);
if (priv->id == 0) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_MISSING_PROPERTY,
_("property is required"));
g_prefix_error(error, "%s.%s: ", NM_SETTING_GENEVE_SETTING_NAME, NM_SETTING_GENEVE_ID);
return FALSE;
}
if (!priv->remote) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_MISSING_PROPERTY,
_("property is required"));
g_prefix_error(error, "%s.%s: ", NM_SETTING_GENEVE_SETTING_NAME, NM_SETTING_GENEVE_REMOTE);
return FALSE;
}
if (!nm_inet_parse_bin(AF_UNSPEC, priv->remote, NULL, NULL)) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid IP address"),
priv->remote);
g_prefix_error(error, "%s.%s: ", NM_SETTING_GENEVE_SETTING_NAME, NM_SETTING_GENEVE_REMOTE);
return FALSE;
}
return TRUE;
}
/*****************************************************************************/
static void
nm_setting_geneve_init(NMSettingGeneve *self)
{}
/**
* nm_setting_geneve_new:
*
* Creates a new #NMSettingGeneve object with default values.
*
* Returns: (transfer full): the new empty #NMSettingGeneve object
*
* Since: 1.58
**/
NMSetting *
nm_setting_geneve_new(void)
{
return g_object_new(NM_TYPE_SETTING_GENEVE, NULL);
}
static void
nm_setting_geneve_class_init(NMSettingGeneveClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
NMSettingClass *setting_class = NM_SETTING_CLASS(klass);
GArray *properties_override = _nm_sett_info_property_override_create_array();
object_class->get_property = _nm_setting_property_get_property_direct;
object_class->set_property = _nm_setting_property_set_property_direct;
setting_class->verify = verify;
/**
* NMSettingGeneve:id:
*
* Specifies the GENEVE Network Identifier (or GENEVE Segment Identifier) to
* use.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_uint32(properties_override,
obj_properties,
NM_SETTING_GENEVE_ID,
PROP_ID,
0,
(1 << 24) - 1,
0,
NM_SETTING_PARAM_INFERRABLE,
NMSettingGenevePrivate,
id);
/**
* NMSettingGeneve:remote:
*
* Specifies the unicast destination IP address to use in outgoing packets
* when communicating with the remote GENEVE tunnel endpoint.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_string(properties_override,
obj_properties,
NM_SETTING_GENEVE_REMOTE,
PROP_REMOTE,
NM_SETTING_PARAM_REQUIRED,
NMSettingGenevePrivate,
remote,
.direct_set_string_ip_address_addr_family =
AF_UNSPEC + 1,
.direct_string_allow_empty = TRUE);
/**
* NMSettingGeneve:destination-port:
*
* Specifies the UDP destination port to communicate to the remote GENEVE
* tunnel endpoint.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_uint32(properties_override,
obj_properties,
NM_SETTING_GENEVE_DESTINATION_PORT,
PROP_DESTINATION_PORT,
0,
G_MAXUINT16,
DST_PORT_DEFAULT,
NM_SETTING_PARAM_INFERRABLE,
NMSettingGenevePrivate,
destination_port);
/**
* NMSettingGeneve:tos:
*
* Specifies the TOS value to use in outgoing packets.
* The special value "inherit" (1) means inherit from outer packet.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_uint32(properties_override,
obj_properties,
NM_SETTING_GENEVE_TOS,
PROP_TOS,
0,
255,
0,
NM_SETTING_PARAM_INFERRABLE,
NMSettingGenevePrivate,
tos);
/**
* NMSettingGeneve:ttl:
*
* Specifies the time-to-live value to use in outgoing packets.
* The special value "inherit" (-1) means inherit from outer packet, 0 means auto, 1-255 are fixed values.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_int32(properties_override,
obj_properties,
NM_SETTING_GENEVE_TTL,
PROP_TTL,
-1,
255,
0,
NM_SETTING_PARAM_INFERRABLE,
NMSettingGenevePrivate,
ttl);
/**
* NMSettingGeneve:df:
*
* Specifies how the Don't Fragment (DF) flag should be handled in the outer IP
* header of GENEVE tunnel packets.
*
* %NM_SETTING_GENEVE_DF_UNSET (0): Don't set the DF flag, packets may be fragmented.
* %NM_SETTING_GENEVE_DF_SET (1): Always set the DF flag, packets will not be fragmented.
* %NM_SETTING_GENEVE_DF_INHERIT (2): Inherit the DF flag from the inner IP header.
*
* Since: 1.58
**/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_GENEVE_DF,
PROP_DF,
NM_TYPE_SETTING_GENEVE_DF,
NM_SETTING_GENEVE_DF_UNSET,
NM_SETTING_PARAM_INFERRABLE,
NULL,
NMSettingGenevePrivate,
df);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit(setting_class,
NM_META_SETTING_TYPE_GENEVE,
NULL,
properties_override,
G_STRUCT_OFFSET(NMSettingGeneve, _priv));
}

View file

@ -5079,6 +5079,9 @@ routing_rules_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
gboolean any = FALSE;
guint i;
if (!_nm_connection_serialize_non_secret(flags))
return NULL;
priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(self);
if (!priv->routing_rules || priv->routing_rules->len == 0)

View file

@ -37,6 +37,7 @@
#include "nm-setting-dcb.h"
#include "nm-setting-dummy.h"
#include "nm-setting-generic.h"
#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hsr.h"
#include "nm-setting-hostname.h"

View file

@ -121,6 +121,7 @@ typedef enum _nm_packed {
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_ETHTOOL,
NM_META_SETTING_TYPE_GENERIC,
NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HOSTNAME,
NM_META_SETTING_TYPE_HSR,

View file

@ -22,6 +22,7 @@ libnm_core_headers = files(
'nm-setting-dummy.h',
'nm-setting-ethtool.h',
'nm-setting-generic.h',
'nm-setting-geneve.h',
'nm-setting-gsm.h',
'nm-setting-hsr.h',
'nm-setting-hostname.h',

View file

@ -206,8 +206,10 @@ NMSettingCdma *nm_connection_get_setting_cdma(NMConnection *connection);
NMSettingConnection *nm_connection_get_setting_connection(NMConnection *connection);
NMSettingDcb *nm_connection_get_setting_dcb(NMConnection *connection);
NM_AVAILABLE_IN_1_8
NMSettingDummy *nm_connection_get_setting_dummy(NMConnection *connection);
NMSettingGeneric *nm_connection_get_setting_generic(NMConnection *connection);
NMSettingDummy *nm_connection_get_setting_dummy(NMConnection *connection);
NMSettingGeneric *nm_connection_get_setting_generic(NMConnection *connection);
NM_AVAILABLE_IN_1_58
NMSettingGeneve *nm_connection_get_setting_geneve(NMConnection *connection);
NMSettingGsm *nm_connection_get_setting_gsm(NMConnection *connection);
NMSettingInfiniband *nm_connection_get_setting_infiniband(NMConnection *connection);
NM_AVAILABLE_IN_1_2

View file

@ -27,6 +27,7 @@ typedef struct _NMSettingConnection NMSettingConnection;
typedef struct _NMSettingDcb NMSettingDcb;
typedef struct _NMSettingDummy NMSettingDummy;
typedef struct _NMSettingEthtool NMSettingEthtool;
typedef struct _NMSettingGeneve NMSettingGeneve;
typedef struct _NMSettingGeneric NMSettingGeneric;
typedef struct _NMSettingGsm NMSettingGsm;
typedef struct _NMSettingHostname NMSettingHostname;

View file

@ -36,6 +36,7 @@
#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge"
#define NM_DBUS_INTERFACE_DEVICE_DUMMY NM_DBUS_INTERFACE_DEVICE ".Dummy"
#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic"
#define NM_DBUS_INTERFACE_DEVICE_GENEVE NM_DBUS_INTERFACE_DEVICE ".Geneve"
#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
#define NM_DBUS_INTERFACE_DEVICE_HSR NM_DBUS_INTERFACE_DEVICE ".Hsr"
#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
@ -250,6 +251,7 @@ typedef enum {
* @NM_DEVICE_TYPE_LOOPBACK: a loopback interface. Since: 1.42.
* @NM_DEVICE_TYPE_HSR: A HSR/PRP device. Since: 1.46.
* @NM_DEVICE_TYPE_IPVLAN: A IPVLAN device. Since: 1.52.
* @NM_DEVICE_TYPE_GENEVE: A GENEVE device. Since: 1.58.
*
* #NMDeviceType values indicate the type of hardware represented by a
* device object.
@ -290,6 +292,7 @@ typedef enum {
NM_DEVICE_TYPE_LOOPBACK = 32,
NM_DEVICE_TYPE_HSR = 33,
NM_DEVICE_TYPE_IPVLAN = 34,
NM_DEVICE_TYPE_GENEVE = 35,
} NMDeviceType;
/**
@ -1245,6 +1248,54 @@ typedef enum /*< flags >*/ {
NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP = 0x1,
} NMDeviceReapplyFlags;
/**
* NMDeviceManaged:
* @NM_DEVICE_MANAGED_NO: the device is not managed.
* @NM_DEVICE_MANAGED_YES: the device is managed.
* @NM_DEVICE_MANAGED_RESET: reset the device managed state to the default value.
*
* Values for the SetManaged() D-Bus call of a device and nm_device_set_managed_async().
*
* Since: 1.58
*/
typedef enum {
NM_DEVICE_MANAGED_NO = 0,
NM_DEVICE_MANAGED_YES = 1,
NM_DEVICE_MANAGED_RESET = 2,
} NMDeviceManaged;
/**
* NMDeviceManagedFlags:
* @NM_DEVICE_MANAGED_FLAGS_NONE: no flag set.
* @NM_DEVICE_MANAGED_FLAGS_RUNTIME: to set the device managed state to the runtime value.
* @NM_DEVICE_MANAGED_FLAGS_PERMANENT: to set the device managed state to the permanent (on disk) value.
* @NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME: to match the device by name, not by MAC address.
* @NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC: to match the device by MAC address, not by name.
* @NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE: to set the administrative state of the
* device to up if the managed state is %NM_DEVICE_MANAGED_YES, and down if the managed state
* is %NM_DEVICE_MANAGED_NO. If the flag is not set, the administrative state is not changed.
* The flag is ignored for %NM_DEVICE_MANAGED_RESET.
* @NM_DEVICE_MANAGED_FLAGS_ALL: all flags.
*
* Flags for the SetManaged() D-Bus call of a device and nm_device_set_managed_async().
*
* %NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME and %NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC
* are mutually exclusive, and they only make sense together with %NM_DEVICE_MANAGED_FLAGS_PERMANENT.
* If none is set, the matching criteria is selected automatically.
*
* Since: 1.58
*/
typedef enum /*< flags >*/ {
NM_DEVICE_MANAGED_FLAGS_NONE = 0,
NM_DEVICE_MANAGED_FLAGS_RUNTIME = 0x1,
NM_DEVICE_MANAGED_FLAGS_PERMANENT = 0x2,
NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_NAME = 0x4,
NM_DEVICE_MANAGED_FLAGS_PERMANENT_BY_MAC = 0x8,
NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE = 0x10,
NM_DEVICE_MANAGED_FLAGS_ALL = 0x1F, /* <skip> */
} NMDeviceManagedFlags;
/**
* NMTernary:
* @NM_TERNARY_DEFAULT: use the globally-configured default value.

View file

@ -0,0 +1,74 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2026 Red Hat, Inc.
*/
#ifndef __NM_SETTING_GENEVE_H__
#define __NM_SETTING_GENEVE_H__
#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION)
#error "Only <NetworkManager.h> can be included directly."
#endif
#include "nm-setting.h"
G_BEGIN_DECLS
#define NM_TYPE_SETTING_GENEVE (nm_setting_geneve_get_type())
#define NM_SETTING_GENEVE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_GENEVE, NMSettingGeneve))
#define NM_SETTING_GENEVE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTING_GENEVE, NMSettingGeneveClass))
#define NM_IS_SETTING_GENEVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_GENEVE))
#define NM_IS_SETTING_GENEVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_GENEVE))
#define NM_SETTING_GENEVE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_SETTING_GENEVE, NMSettingGeneveClass))
#define NM_SETTING_GENEVE_SETTING_NAME "geneve"
#define NM_SETTING_GENEVE_ID "id"
#define NM_SETTING_GENEVE_REMOTE "remote"
#define NM_SETTING_GENEVE_DESTINATION_PORT "destination-port"
#define NM_SETTING_GENEVE_TOS "tos"
#define NM_SETTING_GENEVE_TTL "ttl"
#define NM_SETTING_GENEVE_DF "df"
/**
* NMSettingGeneveDf:
* @NM_SETTING_GENEVE_DF_UNSET: Don't set the DF flag, packets may be fragmented.
* @NM_SETTING_GENEVE_DF_SET: Always set the DF flag, packets will not be fragmented.
* @NM_SETTING_GENEVE_DF_INHERIT: Inherit the DF flag from the inner IP header.
*
* #NMSettingGeneveDf values indicate how the Don't Fragment (DF) flag should be handled
* in the outer IP header of GENEVE tunnel packets.
*
* Since: 1.58
*/
typedef enum {
NM_SETTING_GENEVE_DF_UNSET = 0,
NM_SETTING_GENEVE_DF_SET = 1,
NM_SETTING_GENEVE_DF_INHERIT = 2,
} NMSettingGeneveDf;
typedef struct _NMSettingGeneveClass NMSettingGeneveClass;
NM_AVAILABLE_IN_1_58
GType nm_setting_geneve_get_type(void);
NM_AVAILABLE_IN_1_58
NMSetting *nm_setting_geneve_new(void);
NM_AVAILABLE_IN_1_58
guint nm_setting_geneve_get_id(NMSettingGeneve *setting);
NM_AVAILABLE_IN_1_58
const char *nm_setting_geneve_get_remote(NMSettingGeneve *setting);
NM_AVAILABLE_IN_1_58
guint nm_setting_geneve_get_destination_port(NMSettingGeneve *setting);
NM_AVAILABLE_IN_1_58
guint nm_setting_geneve_get_tos(NMSettingGeneve *setting);
NM_AVAILABLE_IN_1_58
guint nm_setting_geneve_get_ttl(NMSettingGeneve *setting);
NM_AVAILABLE_IN_1_58
NMSettingGeneveDf nm_setting_geneve_get_df(NMSettingGeneve *setting);
G_END_DECLS
#endif /* __NM_SETTING_GENEVE_H__ */

View file

@ -130,6 +130,7 @@ typedef enum {
#define _NM_LINK_TYPE_SW_FIRST NM_LINK_TYPE_BNEP
NM_LINK_TYPE_BNEP, /* Bluetooth Ethernet emulation */
NM_LINK_TYPE_DUMMY,
NM_LINK_TYPE_GENEVE,
NM_LINK_TYPE_GRE,
NM_LINK_TYPE_GRETAP,
NM_LINK_TYPE_IFB,

View file

@ -862,6 +862,7 @@ static const LinkDesc link_descs[] = {
[NM_LINK_TYPE_BNEP] = {"bluetooth", NULL, "bluetooth"},
[NM_LINK_TYPE_DUMMY] = {"dummy", "dummy", NULL},
[NM_LINK_TYPE_GENEVE] = {"geneve", "geneve", "geneve"},
[NM_LINK_TYPE_GRE] = {"gre", "gre", NULL},
[NM_LINK_TYPE_GRETAP] = {"gretap", "gretap", NULL},
[NM_LINK_TYPE_IFB] = {"ifb", "ifb", NULL},
@ -909,6 +910,7 @@ _link_type_from_rtnl_type(const char *name)
NM_LINK_TYPE_BOND, /* "bond" */
NM_LINK_TYPE_BRIDGE, /* "bridge" */
NM_LINK_TYPE_DUMMY, /* "dummy" */
NM_LINK_TYPE_GENEVE, /* "geneve" */
NM_LINK_TYPE_GRE, /* "gre" */
NM_LINK_TYPE_GRETAP, /* "gretap" */
NM_LINK_TYPE_HSR, /* "hsr" */
@ -987,6 +989,7 @@ _link_type_from_devtype(const char *name)
NM_LINK_TYPE_BNEP, /* "bluetooth" */
NM_LINK_TYPE_BOND, /* "bond" */
NM_LINK_TYPE_BRIDGE, /* "bridge" */
NM_LINK_TYPE_GENEVE, /* "geneve" */
NM_LINK_TYPE_HSR, /* "hsr" */
NM_LINK_TYPE_PPP, /* "ppp" */
NM_LINK_TYPE_VLAN, /* "vlan" */
@ -1856,6 +1859,57 @@ _parse_lnk_gre(const char *kind, struct nlattr *info_data)
/*****************************************************************************/
static NMPObject *
_parse_lnk_geneve(const char *kind, struct nlattr *info_data)
{
static const struct nla_policy policy[] = {
[IFLA_GENEVE_ID] = {.type = NLA_U32},
[IFLA_GENEVE_REMOTE] = {.type = NLA_U32},
[IFLA_GENEVE_REMOTE6] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
[IFLA_GENEVE_TTL] = {.type = NLA_U8},
[IFLA_GENEVE_TOS] = {.type = NLA_U8},
[IFLA_GENEVE_TTL_INHERIT] = {.type = NLA_U8},
[IFLA_GENEVE_PORT] = {.type = NLA_U16},
[IFLA_GENEVE_DF] = {.type = NLA_U8},
};
struct nlattr *tb[G_N_ELEMENTS(policy)];
NMPObject *obj;
NMPlatformLnkGeneve *props;
if (!info_data || !nm_streq0(kind, "geneve"))
return NULL;
if (nla_parse_nested_arr(tb, info_data, policy) < 0)
return NULL;
obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_GENEVE, NULL);
props = &obj->lnk_geneve;
if (tb[IFLA_GENEVE_ID])
props->id = nla_get_u32(tb[IFLA_GENEVE_ID]);
if (tb[IFLA_GENEVE_REMOTE])
props->remote = nla_get_u32(tb[IFLA_GENEVE_REMOTE]);
if (tb[IFLA_GENEVE_REMOTE6])
props->remote6 = *nla_data_as(struct in6_addr, tb[IFLA_GENEVE_REMOTE6]);
if (tb[IFLA_GENEVE_TTL_INHERIT] && nla_get_u8(tb[IFLA_GENEVE_TTL_INHERIT]))
props->ttl = -1;
else if (tb[IFLA_GENEVE_TTL])
props->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]);
if (tb[IFLA_GENEVE_TOS])
props->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]);
if (tb[IFLA_GENEVE_PORT])
props->dst_port = ntohs(nla_get_u16(tb[IFLA_GENEVE_PORT]));
if (tb[IFLA_GENEVE_DF])
props->df = nla_get_u8(tb[IFLA_GENEVE_DF]);
return obj;
}
/*****************************************************************************/
static NMPObject *
_parse_lnk_hsr(const char *kind, struct nlattr *info_data)
{
@ -3694,6 +3748,9 @@ _new_from_nl_link(NMPlatform *platform,
case NM_LINK_TYPE_BOND:
lnk_data = _parse_lnk_bond(nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_GENEVE:
lnk_data = _parse_lnk_geneve(nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data);
@ -4033,6 +4090,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
int ifindex;
NMIPAddr gateway;
gboolean is_via;
unsigned rtnh_flags;
} nh = {
.found = FALSE,
.has_more = FALSE,
@ -4142,9 +4200,10 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
v4_nh_extra_nexthops = v4_nh_extra_nexthops_heap;
}
nm_assert(v4_n_nexthops - 1u < v4_nh_extra_alloc);
new_nexthop = &v4_nh_extra_nexthops[v4_n_nexthops - 1u];
new_nexthop->ifindex = rtnh->rtnh_ifindex;
new_nexthop->weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
new_nexthop = &v4_nh_extra_nexthops[v4_n_nexthops - 1u];
new_nexthop->ifindex = rtnh->rtnh_ifindex;
new_nexthop->weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
new_nexthop->rtnh_flags = rtnh->rtnh_flags;
if (rtnh->rtnh_len > sizeof(*rtnh)) {
struct nlattr *ntb[RTA_MAX + 1];
@ -4159,9 +4218,10 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
memcpy(&new_nexthop->gateway, nla_data(ntb[RTA_GATEWAY]), addr_len);
}
} else if (IS_IPv4 || idx == multihop_idx) {
nh.found = TRUE;
nh.ifindex = rtnh->rtnh_ifindex;
nh.weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
nh.found = TRUE;
nh.ifindex = rtnh->rtnh_ifindex;
nh.weight = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
nh.rtnh_flags = rtnh->rtnh_flags;
if (rtnh->rtnh_len > sizeof(*rtnh)) {
struct nlattr *ntb[RTA_MAX + 1];
@ -4409,7 +4469,16 @@ rta_multipath_done:
}
obj->ip_route.r_rtm_flags = rtm->rtm_flags;
obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol);
if (IS_IPv4 && v4_n_nexthops > 1u) {
/* For multipath routes, rtm_flags at the route level does not contain
* RTNH_F_ONLINK. Instead, each nexthop has its own rtnh_flags. Merge the
* first nexthop's RTNH_F_ONLINK into r_rtm_flags, since the first nexthop
* is embedded in the route struct. */
obj->ip_route.r_rtm_flags |= (nh.rtnh_flags & RTNH_F_ONLINK);
}
obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol);
if (nh.has_more) {
parse_nlmsg_iter->iter_more = TRUE;
@ -5182,6 +5251,35 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
nla_nest_end(msg, info_peer);
break;
}
case NM_LINK_TYPE_GENEVE:
{
const NMPlatformLnkGeneve *props = extra_data;
nm_assert(props);
if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
goto nla_put_failure;
NLA_PUT_U32(msg, IFLA_GENEVE_ID, props->id);
if (props->remote) {
NLA_PUT_U32(msg, IFLA_GENEVE_REMOTE, props->remote);
} else if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote6)) {
NLA_PUT(msg, IFLA_GENEVE_REMOTE6, sizeof(props->remote6), &props->remote6);
}
NLA_PUT_U16(msg, IFLA_GENEVE_PORT, htons(props->dst_port));
if (props->ttl == -1) {
NLA_PUT_U8(msg, IFLA_GENEVE_TTL_INHERIT, 1);
} else {
/* When you want to specify a TTL value,
* don't add TTL_INHERIT to the message */
NLA_PUT_U8(msg, IFLA_GENEVE_TTL, props->ttl & 0xff);
}
NLA_PUT_U8(msg, IFLA_GENEVE_TOS, props->tos);
NLA_PUT_U8(msg, IFLA_GENEVE_DF, props->df);
break;
}
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
{
@ -5813,15 +5911,18 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob
}
NLA_PUT_U32(msg, RTA_GATEWAY, gw);
rtnh->rtnh_flags = 0;
if (i == 0u) {
rtnh->rtnh_flags =
(gw != 0 && NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, (unsigned) RTNH_F_ONLINK))
? RTNH_F_ONLINK
: 0;
} else {
const NMPlatformIP4RtNextHop *n2 = &obj->_ip4_route.extra_nexthops[i - 1u];
if (obj->ip4_route.n_nexthops > 1
&& NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, (unsigned) (RTNH_F_ONLINK)) && gw != 0) {
/* Unlike kernel, we only track the onlink flag per NMPlatformIP4Address, and
* not per nexthop. That is fine for NetworkManager configuring addresses.
* It is not fine for tracking addresses from kernel in platform cache,
* because the rtnh_flags of the nexthops need to be part of nmp_object_id_cmp(). */
rtnh->rtnh_flags |= RTNH_F_ONLINK;
rtnh->rtnh_flags =
(gw != 0 && NM_FLAGS_HAS(n2->rtnh_flags, (unsigned) RTNH_F_ONLINK))
? RTNH_F_ONLINK
: 0;
}
rtnh->rtnh_len = (char *) nlmsg_tail(nlmsg_hdr(msg)) - (char *) rtnh;

View file

@ -1388,6 +1388,12 @@ nm_platform_link_add(NMPlatform *self,
case NM_LINK_TYPE_VETH:
nm_sprintf_buf(buf, ", veth-peer \"%s\"", (const char *) extra_data);
break;
case NM_LINK_TYPE_GENEVE:
nm_strbuf_append_str(&buf_p, &buf_len, ", ");
nm_platform_lnk_geneve_to_string((const NMPlatformLnkGeneve *) extra_data,
buf_p,
buf_len);
break;
case NM_LINK_TYPE_GRE:
case NM_LINK_TYPE_GRETAP:
nm_strbuf_append_str(&buf_p, &buf_len, ", ");
@ -2565,6 +2571,12 @@ nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformL
return _link_get_lnk(self, ifindex, NM_LINK_TYPE_BRIDGE, out_link);
}
const NMPlatformLnkGeneve *
nm_platform_link_get_lnk_geneve(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
return _link_get_lnk(self, ifindex, NM_LINK_TYPE_GENEVE, out_link);
}
const NMPlatformLnkGre *
nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
@ -3211,7 +3223,6 @@ gboolean
nm_platform_link_veth_get_properties(NMPlatform *self, int ifindex, int *out_peer_ifindex)
{
const NMPlatformLink *plink;
int peer_ifindex;
plink = nm_platform_link_get(self, ifindex);
if (!plink)
@ -3220,23 +3231,10 @@ nm_platform_link_veth_get_properties(NMPlatform *self, int ifindex, int *out_pee
if (plink->type != NM_LINK_TYPE_VETH)
return FALSE;
if (plink->parent != 0) {
NM_SET_OUT(out_peer_ifindex, plink->parent);
return TRUE;
}
if (plink->parent == 0)
return FALSE;
/* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */
if (out_peer_ifindex) {
nm_auto_pop_netns NMPNetns *netns = NULL;
if (!nm_platform_netns_push(self, &netns))
return FALSE;
peer_ifindex = nmp_ethtool_ioctl_get_peer_ifindex(plink->ifindex);
if (peer_ifindex <= 0)
return FALSE;
*out_peer_ifindex = peer_ifindex;
}
NM_SET_OUT(out_peer_ifindex, plink->parent);
return TRUE;
}
@ -6494,6 +6492,52 @@ nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize le
return buf;
}
const char *
nm_platform_lnk_geneve_to_string(const NMPlatformLnkGeneve *lnk, char *buf, gsize len)
{
char str_remote[NM_INET_ADDRSTRLEN];
char str_remote1[30 + NM_INET_ADDRSTRLEN];
char str_remote6[NM_INET_ADDRSTRLEN];
char str_remote6_1[30 + NM_INET_ADDRSTRLEN];
char str_ttl[30];
char str_tos[30];
char str_id[30];
char str_dstport[30];
if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len))
return buf;
g_snprintf(
buf,
len,
"geneve"
"%s" /* id */
"%s" /* remote */
"%s" /* remote6 */
"%s" /* dst_port */
"%s" /* ttl */
"%s" /* tos */
"%s" /* df */
"",
lnk->id ? nm_sprintf_buf(str_id, " id %u", lnk->id) : "",
lnk->remote
? nm_sprintf_buf(str_remote, " remote %s", nm_inet4_ntop(lnk->remote, str_remote1))
: "",
!IN6_IS_ADDR_UNSPECIFIED(&lnk->remote6)
? nm_sprintf_buf(str_remote6, " remote %s", nm_inet6_ntop(&lnk->remote6, str_remote6_1))
: "",
lnk->dst_port ? nm_sprintf_buf(str_dstport, " dstport %u", lnk->dst_port) : "",
lnk->ttl > 0 ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl & 0xff)
: lnk->ttl == 0 ? "ttl auto"
: "ttl inherit",
lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos))
: "",
lnk->df == 1 ? " df set "
: lnk->df == 2 ? " df inherit "
: "");
return buf;
}
const char *
nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len)
{
@ -7366,13 +7410,16 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
"%s" /* ifindex */
"%s%s" /* gateway */
" weight %s" /* weight */
"%s" /* onlink */
"",
NM_PRINT_FMT_QUOTED2(nexthop->gateway != 0 || nexthop->ifindex <= 0,
" via ",
nm_inet4_ntop(nexthop->gateway, s_gateway),
""),
_to_string_dev(str_dev, nexthop->ifindex),
nm_sprintf_buf(weight_str, "%u", nexthop->weight));
nm_sprintf_buf(weight_str, "%u", nexthop->weight),
NM_FLAGS_HAS(nexthop->rtnh_flags, (unsigned) RTNH_F_ONLINK) ? " onlink"
: "");
}
}
}
@ -8491,6 +8538,27 @@ nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b)
return 0;
}
void
nm_platform_lnk_geneve_hash_update(const NMPlatformLnkGeneve *obj, NMHashState *h)
{
nm_hash_update_vals(h, obj->id, obj->remote, obj->dst_port, obj->ttl, obj->tos, obj->df);
nm_hash_update_mem(h, &obj->remote6, sizeof(obj->remote6));
}
int
nm_platform_lnk_geneve_cmp(const NMPlatformLnkGeneve *a, const NMPlatformLnkGeneve *b)
{
NM_CMP_SELF(a, b);
NM_CMP_FIELD(a, b, id);
NM_CMP_FIELD(a, b, remote);
NM_CMP_FIELD_MEMCMP(a, b, remote6);
NM_CMP_FIELD(a, b, ttl);
NM_CMP_FIELD(a, b, tos);
NM_CMP_FIELD(a, b, dst_port);
NM_CMP_FIELD(a, b, df);
return 0;
}
void
nm_platform_lnk_hsr_hash_update(const NMPlatformLnkHsr *obj, NMHashState *h)
{
@ -8931,7 +8999,11 @@ nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj,
nm_assert(obj);
w = for_id ? NM_MAX(obj->weight, 1u) : obj->weight;
nm_hash_update_vals(h, obj->ifindex, obj->gateway, w);
nm_hash_update_vals(h,
obj->ifindex,
obj->gateway,
w,
for_id ? (obj->rtnh_flags & RTNH_F_ONLINK) : obj->rtnh_flags);
}
void
@ -8968,7 +9040,6 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
obj->initrwnd,
obj->mtu,
obj->rto_min,
obj->r_rtm_flags & RTNH_F_ONLINK,
NM_HASH_COMBINE_BOOLS(guint16,
obj->quickack,
obj->lock_window,
@ -8986,7 +9057,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
obj->via.addr_family == AF_INET6 ? obj->via.addr.addr6
: in6addr_any,
obj->gateway,
_ip4_route_weight_normalize(n_nexthops, obj->weight, FALSE));
_ip4_route_weight_normalize(n_nexthops, obj->weight, FALSE),
obj->r_rtm_flags & RTNH_F_ONLINK);
}
}
break;
@ -9079,6 +9151,11 @@ nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a,
w_b = for_id ? NM_MAX(b->weight, 1u) : b->weight;
NM_CMP_DIRECT(w_a, w_b);
if (for_id)
NM_CMP_DIRECT(a->rtnh_flags & RTNH_F_ONLINK, b->rtnh_flags & RTNH_F_ONLINK);
else
NM_CMP_FIELD(a, b, rtnh_flags);
return 0;
}
@ -9119,12 +9196,6 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
NM_CMP_FIELD(a, b, mtu);
NM_CMP_FIELD(a, b, rto_min);
/* Note that for NetworkManager, the onlink flag is only part of the entire route.
* For kernel, each next hop has it's own onlink flag (rtnh_flags). This means,
* we can only merge ECMP routes, if they agree with their onlink flag, and then
* all next hops are onlink (or not). */
NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK);
NM_CMP_FIELD_UNSAFE(a, b, quickack);
NM_CMP_FIELD_UNSAFE(a, b, lock_window);
NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd);
@ -9143,6 +9214,10 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
NM_CMP_DIRECT(n_nexthops, nm_platform_ip4_route_get_n_nexthops(b));
NM_CMP_DIRECT(_ip4_route_weight_normalize(n_nexthops, a->weight, FALSE),
_ip4_route_weight_normalize(n_nexthops, b->weight, FALSE));
/* The onlink flag is per-nexthop. For the first nexthop it is in
* r_rtm_flags. For extra nexthops, it's compared via
* nm_platform_ip4_rt_nexthop_cmp(). */
NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK);
}
}
break;

View file

@ -287,7 +287,7 @@ guint _nm_platform_signal_id_get(NMPlatformSignalIdType signal_type);
#define __NMPlatformIPRoute_COMMON \
__NMPlatformObjWithIfindex_COMMON; \
\
/* rtnh_flags
/* rtm_flags and rtnh_flags
*
* Routes with rtm_flags RTM_F_CLONED are hidden by platform and
* do not exist from the point-of-view of platform users.
@ -295,8 +295,15 @@ guint _nm_platform_signal_id_get(NMPlatformSignalIdType signal_type);
*
* NOTE: currently we ignore all flags except RTM_F_CLONED
* and RTNH_F_ONLINK.
* We also may not properly consider the flags as part of the ID
* in route-cmp. */ \
*
* For IPv4 routes, the RTNH_F_ONLINK flag here applies to the
* first nexthop (which is embedded in the route struct). Extra
* nexthops (NMPlatformIP4RtNextHop) each have their own
* rtnh_flags field.
*
* For single-hop routes, this field comes directly from
* rtm_flags. For multi-hop routes from kernel, the first
* nexthop's RTNH_F_ONLINK from rtnh_flags is merged here. */ \
unsigned r_rtm_flags; \
\
/* RTA_METRICS.RTAX_ADVMSS (iproute2: advmss) */ \
@ -733,10 +740,10 @@ typedef struct {
*/
guint16 weight;
/* FIXME: each next hop in kernel also has a rtnh_flags (for example to
* set RTNH_F_ONLINK). As the next hop is part of the identifier of an
* IPv4 route, so is their flags. We must also track the flag, otherwise
* two routes that look different for kernel, get merged by platform cache. */
/* Each next hop in kernel has its own rtnh_flags (for example to
* set RTNH_F_ONLINK). The flags are part of the identifier of a
* route. */
unsigned rtnh_flags;
} NMPlatformIP4RtNextHop;
typedef struct {
@ -835,6 +842,16 @@ typedef struct {
bool use_carrier : 1;
} _nm_alignas(NMPlatformObject) NMPlatformLnkBond;
typedef struct {
struct in6_addr remote6;
in_addr_t remote;
guint32 id;
gint32 ttl;
guint16 dst_port;
guint8 tos;
guint8 df;
} _nm_alignas(NMPlatformObject) NMPlatformLnkGeneve;
typedef struct {
int parent_ifindex;
in_addr_t local;
@ -1858,6 +1875,15 @@ nm_platform_link_vxlan_add(NMPlatform *self,
return nm_platform_link_add(self, NM_LINK_TYPE_VXLAN, name, 0, NULL, 0, 0, props, out_link);
}
static inline int
nm_platform_link_geneve_add(NMPlatform *self,
const char *name,
const NMPlatformLnkGeneve *props,
const NMPlatformLink **out_link)
{
return nm_platform_link_add(self, NM_LINK_TYPE_GENEVE, name, 0, NULL, 0, 0, props, out_link);
}
static inline int
nm_platform_link_6lowpan_add(NMPlatform *self,
const char *name,
@ -2143,6 +2169,8 @@ const NMPlatformLnkBond *
nm_platform_link_get_lnk_bond(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkBridge *
nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGeneve *
nm_platform_link_get_lnk_geneve(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *
nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkGre *
@ -2482,6 +2510,7 @@ gboolean nm_platform_tc_sync(NMPlatform *self,
const char *nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len);
const char *nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize len);
const char *nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len);
const char *nm_platform_lnk_geneve_to_string(const NMPlatformLnkGeneve *lnk, char *buf, gsize len);
const char *nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len);
const char *nm_platform_lnk_hsr_to_string(const NMPlatformLnkHsr *lnk, char *buf, gsize len);
const char *
@ -2537,6 +2566,7 @@ nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *bu
int nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b);
int nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b);
int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b);
int nm_platform_lnk_geneve_cmp(const NMPlatformLnkGeneve *a, const NMPlatformLnkGeneve *b);
int nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b);
int nm_platform_lnk_hsr_cmp(const NMPlatformLnkHsr *a, const NMPlatformLnkHsr *b);
int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a,
@ -2612,6 +2642,7 @@ void nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj,
NMHashState *h);
void nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h);
void nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h);
void nm_platform_lnk_geneve_hash_update(const NMPlatformLnkGeneve *obj, NMHashState *h);
void nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h);
void nm_platform_lnk_hsr_hash_update(const NMPlatformLnkHsr *obj, NMHashState *h);
void nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h);

View file

@ -166,6 +166,7 @@ typedef enum _nm_packed {
NMP_OBJECT_TYPE_TFILTER,
NMP_OBJECT_TYPE_LNK_BRIDGE,
NMP_OBJECT_TYPE_LNK_GENEVE,
NMP_OBJECT_TYPE_LNK_GRE,
NMP_OBJECT_TYPE_LNK_GRETAP,
NMP_OBJECT_TYPE_LNK_HSR,
@ -194,15 +195,15 @@ typedef enum _nm_packed {
NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,
} NMPObjectType;
static inline guint32
static inline guint64
nmp_object_type_to_flags(NMPObjectType obj_type)
{
G_STATIC_ASSERT_EXPR(NMP_OBJECT_TYPE_MAX < 32);
G_STATIC_ASSERT_EXPR(NMP_OBJECT_TYPE_MAX < 64);
nm_assert(_NM_INT_NOT_NEGATIVE(obj_type));
nm_assert(obj_type < NMP_OBJECT_TYPE_MAX);
return ((guint32) 1u) << obj_type;
return ((guint64) 1u) << obj_type;
}
/*****************************************************************************/

View file

@ -1190,33 +1190,6 @@ nmp_ethtool_ioctl_supports_vlans(int ifindex)
return !(features->features[block].active & (1 << bit));
}
int
nmp_ethtool_ioctl_get_peer_ifindex(int ifindex)
{
nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex);
gsize stats_len;
gs_free struct ethtool_stats *stats_free = NULL;
struct ethtool_stats *stats;
int peer_ifindex_stat;
g_return_val_if_fail(ifindex > 0, 0);
peer_ifindex_stat = ethtool_get_stringset_index(&shandle, ETH_SS_STATS, "peer_ifindex");
if (peer_ifindex_stat < 0) {
nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex);
return FALSE;
}
stats_len = sizeof(*stats) + (peer_ifindex_stat + 1) * sizeof(guint64);
stats = nm_malloc0_maybe_a(300, stats_len, &stats_free);
stats->cmd = ETHTOOL_GSTATS;
stats->n_stats = peer_ifindex_stat + 1;
if (_ethtool_call_handle(&shandle, stats, stats_len) < 0)
return 0;
return stats->data[peer_ifindex_stat];
}
gboolean
nmp_ethtool_ioctl_get_wake_on_lan(int ifindex)
{

View file

@ -9,8 +9,6 @@ gboolean nmp_ethtool_ioctl_supports_carrier_detect(int ifindex);
gboolean nmp_ethtool_ioctl_supports_vlans(int ifindex);
int nmp_ethtool_ioctl_get_peer_ifindex(int ifindex);
gboolean nmp_ethtool_ioctl_get_wake_on_lan(int ifindex);
gboolean nmp_ethtool_ioctl_set_wake_on_lan(int ifindex,

View file

@ -3469,6 +3469,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bridge_hash_update,
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bridge_cmp,
},
[NMP_OBJECT_TYPE_LNK_GENEVE - 1] =
{
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),
.obj_type = NMP_OBJECT_TYPE_LNK_GENEVE,
.sizeof_data = sizeof(NMPObjectLnkGeneve),
.sizeof_public = sizeof(NMPlatformLnkGeneve),
.obj_type_name = "geneve",
.lnk_link_type = NM_LINK_TYPE_GENEVE,
.cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_geneve_to_string,
.cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_geneve_hash_update,
.cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_geneve_cmp,
},
[NMP_OBJECT_TYPE_LNK_GRE - 1] =
{
.parent = DEDUP_MULTI_OBJ_CLASS_INIT(),

View file

@ -250,6 +250,10 @@ typedef struct {
NMPlatformLnkBond _public;
} NMPObjectLnkBond;
typedef struct {
NMPlatformLnkGeneve _public;
} NMPObjectLnkGeneve;
typedef struct {
NMPlatformLnkGre _public;
} NMPObjectLnkGre;
@ -383,6 +387,9 @@ struct _NMPObject {
NMPlatformLnkBond lnk_bond;
NMPObjectLnkBond _lnk_bond;
NMPlatformLnkGeneve lnk_geneve;
NMPObjectLnkGeneve _lnk_geneve;
NMPlatformLnkGre lnk_gre;
NMPObjectLnkGre _lnk_gre;
@ -543,6 +550,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
case NMP_OBJECT_TYPE_LNK_BRIDGE:
case NMP_OBJECT_TYPE_LNK_BOND:
case NMP_OBJECT_TYPE_LNK_GENEVE:
case NMP_OBJECT_TYPE_LNK_GRE:
case NMP_OBJECT_TYPE_LNK_GRETAP:
case NMP_OBJECT_TYPE_LNK_HSR:

View file

@ -19,6 +19,7 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Route))
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLink));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBond));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBridge));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGeneve));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGre));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkInfiniband));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIp6Tnl));
@ -49,6 +50,7 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXRoute)
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLink));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBond));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBridge));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGeneve));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGre));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkInfiniband));
G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIp6Tnl));

View file

@ -191,6 +191,17 @@ nm_meta_abstract_info_get_name(const NMMetaAbstractInfo *abstract_info, gboolean
return n;
}
const char *
nm_meta_abstract_info_get_alias(const NMMetaAbstractInfo *abstract_info)
{
nm_assert(abstract_info);
nm_assert(abstract_info->meta_type);
if (abstract_info->meta_type->get_alias)
return abstract_info->meta_type->get_alias(abstract_info);
return NULL;
}
const NMMetaAbstractInfo *const *
nm_meta_abstract_info_get_nested(const NMMetaAbstractInfo *abstract_info,
guint *out_len,
@ -466,10 +477,15 @@ _output_selection_select_one(const NMMetaAbstractInfo *const *fields_array,
for (i = 0; fields_array[i]; i++) {
const NMMetaAbstractInfo *fi = fields_array[i];
const char *name, *alias;
const NMMetaAbstractInfo *const *nested;
gs_free gpointer nested_to_free = NULL;
if (g_ascii_strcasecmp(i_name, nm_meta_abstract_info_get_name(fi, FALSE)) != 0)
name = nm_meta_abstract_info_get_name(fi, FALSE);
alias = nm_meta_abstract_info_get_alias(fi);
if (g_ascii_strcasecmp(i_name, name) != 0
&& (alias == NULL || g_ascii_strcasecmp(i_name, alias) != 0))
continue;
if (!right || !validate_nested) {
@ -480,8 +496,11 @@ _output_selection_select_one(const NMMetaAbstractInfo *const *fields_array,
nested = nm_meta_abstract_info_get_nested(fi, NULL, &nested_to_free);
if (nested) {
for (j = 0; nested[j]; nested++) {
if (g_ascii_strcasecmp(right, nm_meta_abstract_info_get_name(nested[j], FALSE))
== 0) {
name = nm_meta_abstract_info_get_name(nested[j], FALSE);
alias = nm_meta_abstract_info_get_alias(nested[j]);
if (g_ascii_strcasecmp(right, name) == 0
|| (alias && g_ascii_strcasecmp(right, alias) == 0)) {
found = TRUE;
break;
}

View file

@ -36,6 +36,7 @@ const NMMetaSettingInfoEditor *const *nm_meta_setting_infos_editor_p(void);
const char *nm_meta_abstract_info_get_name(const NMMetaAbstractInfo *abstract_info,
gboolean for_header);
const char *nm_meta_abstract_info_get_alias(const NMMetaAbstractInfo *abstract_info);
const NMMetaAbstractInfo *const *
nm_meta_abstract_info_get_nested(const NMMetaAbstractInfo *abstract_info,

View file

@ -27,6 +27,7 @@
#include "nm-setting-dummy.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-generic.h"
#include "nm-setting-geneve.h"
#include "nm-setting-gsm.h"
#include "nm-setting-hostname.h"
#include "nm-setting-hsr.h"
@ -324,6 +325,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_GENERIC_SETTING_NAME,
.get_setting_gtype = nm_setting_generic_get_type,
},
[NM_META_SETTING_TYPE_GENEVE] =
{
.meta_type = NM_META_SETTING_TYPE_GENEVE,
.setting_priority = NM_SETTING_PRIORITY_HW_BASE,
.setting_name = NM_SETTING_GENEVE_SETTING_NAME,
.get_setting_gtype = nm_setting_geneve_get_type,
},
[NM_META_SETTING_TYPE_GSM] =
{
.meta_type = NM_META_SETTING_TYPE_GSM,
@ -655,6 +663,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
NM_META_SETTING_TYPE_CDMA,
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_GENERIC,
NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HSR,
NM_META_SETTING_TYPE_INFINIBAND,

View file

@ -121,6 +121,7 @@ typedef enum _nm_packed {
NM_META_SETTING_TYPE_DUMMY,
NM_META_SETTING_TYPE_ETHTOOL,
NM_META_SETTING_TYPE_GENERIC,
NM_META_SETTING_TYPE_GENEVE,
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_HOSTNAME,
NM_META_SETTING_TYPE_HSR,

View file

@ -6112,6 +6112,57 @@ static const NMMetaPropertyInfo *const property_infos_GENERIC[] = {
NULL
};
#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GENEVE
static const NMMetaPropertyInfo *const property_infos_GENEVE[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_ID,
.is_cli_option = TRUE,
.property_alias = "id",
.inf_flags = NM_META_PROPERTY_INF_FLAG_REQD,
.prompt = N_("GENEVE ID"),
.property_type = &_pt_gobject_int,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_REMOTE,
.is_cli_option = TRUE,
.property_alias = "remote",
.inf_flags = NM_META_PROPERTY_INF_FLAG_REQD,
.prompt = N_("Remote"),
.property_type = &_pt_gobject_string,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_DESTINATION_PORT,
.is_cli_option = TRUE,
.property_alias = "destination-port",
.prompt = N_("Destination port"),
.property_type = &_pt_gobject_int,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_TOS,
.property_type = &_pt_gobject_int,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
.value_infos = INT_VALUE_INFOS (
{
.value.u64 = 1,
.nick = "inherit",
},
),
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_TTL,
.property_type = &_pt_gobject_int,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
.value_infos = INT_VALUE_INFOS (
{
.value.i64 = -1,
.nick = "inherit",
},
),
),
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_GENEVE_DF,
.property_type = &_pt_gobject_enum,
),
NULL
};
#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_GSM
static const NMMetaPropertyInfo *const property_infos_GSM[] = {
@ -9018,6 +9069,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
#define SETTING_PRETTY_NAME_DUMMY N_("Dummy settings")
#define SETTING_PRETTY_NAME_ETHTOOL N_("Ethtool settings")
#define SETTING_PRETTY_NAME_GENERIC N_("Generic settings")
#define SETTING_PRETTY_NAME_GENEVE N_("Geneve settings")
#define SETTING_PRETTY_NAME_GSM N_("GSM mobile broadband connection")
#define SETTING_PRETTY_NAME_HOSTNAME N_("Hostname settings")
#define SETTING_PRETTY_NAME_HSR N_("HSR settings")
@ -9156,6 +9208,14 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
NM_META_SETTING_VALID_PART_ITEM (GENERIC, TRUE),
),
),
SETTING_INFO (GENEVE,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
NM_META_SETTING_VALID_PART_ITEM (GENEVE, TRUE),
NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE),
NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE),
),
),
SETTING_INFO (GSM,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),

View file

@ -482,6 +482,7 @@ struct _NMMetaType {
NMObjBaseClass parent;
const char *type_name;
const char *(*get_name)(const NMMetaAbstractInfo *abstract_info, gboolean for_header);
const char *(*get_alias)(const NMMetaAbstractInfo *abstract_info);
const NMMetaAbstractInfo *const *(*get_nested)(const NMMetaAbstractInfo *abstract_info,
guint *out_len,
gpointer *out_to_free);

View file

@ -499,6 +499,12 @@
#define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_PRIO N_("The port priority for bond active port re-selection during failover. A higher number means a higher priority in selection. The primary port has the highest priority. This option is only compatible with active-backup, balance-tlb and balance-alb modes.")
#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_QUEUE_ID N_("The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device.")
#define DESCRIBE_DOC_NM_SETTING_GENEVE_DESTINATION_PORT N_("Specifies the UDP destination port to communicate to the remote GENEVE tunnel endpoint.")
#define DESCRIBE_DOC_NM_SETTING_GENEVE_DF N_("Specifies how the Don't Fragment (DF) flag should be handled in the outer IP header of GENEVE tunnel packets. \"unset\" (0) (0): Don't set the DF flag, packets may be fragmented. \"set\" (1) (1): Always set the DF flag, packets will not be fragmented. \"inherit\" (2) (2): Inherit the DF flag from the inner IP header.")
#define DESCRIBE_DOC_NM_SETTING_GENEVE_ID N_("Specifies the GENEVE Network Identifier (or GENEVE Segment Identifier) to use.")
#define DESCRIBE_DOC_NM_SETTING_GENEVE_REMOTE N_("Specifies the unicast destination IP address to use in outgoing packets when communicating with the remote GENEVE tunnel endpoint.")
#define DESCRIBE_DOC_NM_SETTING_GENEVE_TOS N_("Specifies the TOS value to use in outgoing packets. The special value \"inherit\" (1) means inherit from outer packet.")
#define DESCRIBE_DOC_NM_SETTING_GENEVE_TTL N_("Specifies the time-to-live value to use in outgoing packets. The special value \"inherit\" (-1) means inherit from outer packet, 0 means auto, 1-255 are fixed values.")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DHCP N_("Whether the system hostname can be determined from DHCP on this connection. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"true\" (1).")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"true\" (1).")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to \"true\" (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to \"false\" (0), the hostname can be set from this device even if it doesn't have the default route. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"false\" (0).")

View file

@ -4,6 +4,7 @@
*/
#include "libnm-client-aux-extern/nm-default-client.h"
#include "nmcli.h"
#include "connections.h"
@ -817,8 +818,11 @@ _metagen_con_show_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS)
}
const NmcMetaGenericInfo *const metagen_con_show[_NMC_GENERIC_INFO_TYPE_CON_SHOW_NUM + 1] = {
#define _METAGEN_CON_SHOW(type, name) \
[type] = NMC_META_GENERIC(name, .info_type = type, .get_fcn = _metagen_con_show_get_fcn)
#define _METAGEN_CON_SHOW(type, name, ...) \
[type] = NMC_META_GENERIC(name, \
.info_type = type, \
.get_fcn = _metagen_con_show_get_fcn, \
__VA_ARGS__)
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_NAME, "NAME"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_UUID, "UUID"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_TYPE, "TYPE"),
@ -832,7 +836,7 @@ const NmcMetaGenericInfo *const metagen_con_show[_NMC_GENERIC_INFO_TYPE_CON_SHOW
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_DEVICE, "DEVICE"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_STATE, "STATE"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE_PATH, "ACTIVE-PATH"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_PORT, "SLAVE"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_PORT, "PORT", .alias = "SLAVE"),
_METAGEN_CON_SHOW(NMC_GENERIC_INFO_TYPE_CON_SHOW_FILENAME, "FILENAME"),
};
#define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE"
@ -919,6 +923,7 @@ _metagen_con_active_general_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS)
* but the settings-connection profile. There is no guarantee, that they agree. */
return s_con ? nm_setting_connection_get_zone(s_con) : NULL;
case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CONTROLLER_PATH:
case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_MASTER_PATH:
dev = nm_active_connection_get_controller(ac);
return dev ? nm_object_get_path(NM_OBJECT(dev)) : NULL;
default:
@ -948,6 +953,8 @@ const NmcMetaGenericInfo
_METAGEN_CON_ACTIVE_GENERAL(NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CON_PATH, "CON-PATH"),
_METAGEN_CON_ACTIVE_GENERAL(NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_ZONE, "ZONE"),
_METAGEN_CON_ACTIVE_GENERAL(NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CONTROLLER_PATH,
"CONTROLLER-PATH"),
_METAGEN_CON_ACTIVE_GENERAL(NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_MASTER_PATH,
"MASTER-PATH"),
};
@ -1070,13 +1077,13 @@ const NmcMetaGenericInfo
"," NM_SETTING_DCB_SETTING_NAME "," NM_SETTING_TUN_SETTING_NAME \
"," NM_SETTING_IP_TUNNEL_SETTING_NAME "," NM_SETTING_MACSEC_SETTING_NAME \
"," NM_SETTING_MACVLAN_SETTING_NAME "," NM_SETTING_VXLAN_SETTING_NAME \
"," NM_SETTING_VRF_SETTING_NAME "," NM_SETTING_WPAN_SETTING_NAME \
"," NM_SETTING_6LOWPAN_SETTING_NAME "," NM_SETTING_WIREGUARD_SETTING_NAME \
"," NM_SETTING_LINK_SETTING_NAME "," NM_SETTING_PROXY_SETTING_NAME \
"," NM_SETTING_TC_CONFIG_SETTING_NAME "," NM_SETTING_SRIOV_SETTING_NAME \
"," NM_SETTING_ETHTOOL_SETTING_NAME "," NM_SETTING_OVS_DPDK_SETTING_NAME \
"," NM_SETTING_HOSTNAME_SETTING_NAME "," NM_SETTING_HSR_SETTING_NAME \
"," NM_SETTING_IPVLAN_SETTING_NAME
"," NM_SETTING_GENEVE_SETTING_NAME "," NM_SETTING_VRF_SETTING_NAME \
"," NM_SETTING_WPAN_SETTING_NAME "," NM_SETTING_6LOWPAN_SETTING_NAME \
"," NM_SETTING_WIREGUARD_SETTING_NAME "," NM_SETTING_LINK_SETTING_NAME \
"," NM_SETTING_PROXY_SETTING_NAME "," NM_SETTING_TC_CONFIG_SETTING_NAME \
"," NM_SETTING_SRIOV_SETTING_NAME "," NM_SETTING_ETHTOOL_SETTING_NAME \
"," NM_SETTING_OVS_DPDK_SETTING_NAME "," NM_SETTING_HOSTNAME_SETTING_NAME \
"," NM_SETTING_HSR_SETTING_NAME "," NM_SETTING_IPVLAN_SETTING_NAME
/* NM_SETTING_DUMMY_SETTING_NAME NM_SETTING_WIMAX_SETTING_NAME */
const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[] = {
@ -1306,6 +1313,9 @@ usage_connection_add(void)
" [source-port-min <0-65535>]\n"
" [source-port-max <0-65535>]\n"
" [destination-port <0-65535>]\n\n"
" geneve: id <GENEVE ID>\n"
" remote <IP address>\n"
" [destination-port <0-65535>]\n\n"
" wpan: [short-addr <0x0000-0xffff>]\n"
" [pan-id <0x0000-0xffff>]\n"
" [page <default|0-31>]\n"

View file

@ -766,17 +766,19 @@ const NmcMetaGenericInfo *const nmc_fields_dev_wimax_list[] = {
const NmcMetaGenericInfo *const nmc_fields_dev_show_controller_prop[] = {
NMC_META_GENERIC("NAME"), /* 0 */
NMC_META_GENERIC("SLAVES"), /* 1 */
NMC_META_GENERIC("PORTS"), /* 2 */
NULL,
};
#define NMC_FIELDS_DEV_SHOW_CONTROLLER_PROP_COMMON "NAME,SLAVES"
#define NMC_FIELDS_DEV_SHOW_CONTROLLER_PROP_COMMON "NAME,SLAVES,PORTS"
const NmcMetaGenericInfo *const nmc_fields_dev_show_team_prop[] = {
NMC_META_GENERIC("NAME"), /* 0 */
NMC_META_GENERIC("SLAVES"), /* 1 */
NMC_META_GENERIC("CONFIG"), /* 2 */
NMC_META_GENERIC("PORTS"), /* 2 */
NMC_META_GENERIC("CONFIG"), /* 3 */
NULL,
};
#define NMC_FIELDS_DEV_SHOW_TEAM_PROP_COMMON "NAME,SLAVES,CONFIG"
#define NMC_FIELDS_DEV_SHOW_TEAM_PROP_COMMON "NAME,SLAVES,PORTS,CONFIG"
const NmcMetaGenericInfo *const nmc_fields_dev_show_vlan_prop[] = {
NMC_META_GENERIC("NAME"), /* 0 */
@ -855,7 +857,8 @@ usage(void)
"delete | monitor | wifi | lldp }\n\n"
" status\n\n"
" show [<ifname>]\n\n"
" set [ifname] <ifname> [autoconnect yes|no] [managed yes|no]\n\n"
" set [ifname] <ifname> [autoconnect yes|no] [managed [--permanent|--permanent-only] "
"yes|no|up|down|reset]\n\n"
" connect <ifname>\n\n"
" reapply <ifname>\n\n"
" modify <ifname> ([+|-]<setting>.<property> <value>)+\n\n"
@ -973,14 +976,15 @@ usage_device_delete(void)
static void
usage_device_set(void)
{
nmc_printerr(_("Usage: nmcli device set { ARGUMENTS | help }\n"
"\n"
"ARGUMENTS := DEVICE { PROPERTY [ PROPERTY ... ] }\n"
"DEVICE := [ifname] <ifname> \n"
"PROPERTY := { autoconnect { yes | no } |\n"
" { managed { yes | no }\n"
"\n"
"Modify device properties.\n\n"));
nmc_printerr(_(
"Usage: nmcli device set { ARGUMENTS | help }\n"
"\n"
"ARGUMENTS := DEVICE { PROPERTY [ PROPERTY ... ] }\n"
"DEVICE := [ifname] <ifname> \n"
"PROPERTY := { autoconnect { yes | no } |\n"
" { managed [--permanent | --permanent-only] { yes | no | up | down | reset }\n"
"\n"
"Modify device properties.\n\n"));
}
static void
@ -1537,7 +1541,8 @@ print_bond_bridge_info(NMDevice *device,
arr = nmc_dup_fields_array(tmpl, NMC_OF_FLAG_SECTION_PREFIX);
set_val_strc(arr, 0, group_prefix); /* i.e. BOND, TEAM, BRIDGE */
set_val_str(arr, 1, g_string_free(ports_str, FALSE));
set_val_str(arr, 1, g_strdup(ports_str->str));
set_val_str(arr, 2, g_string_free(ports_str, FALSE));
g_ptr_array_add(out.output_data, arr);
print_data_prepare_width(out.output_data);
@ -1600,8 +1605,9 @@ print_team_info(NMDevice *device, NmCli *nmc, const char *group_prefix, const ch
arr = nmc_dup_fields_array(tmpl, NMC_OF_FLAG_SECTION_PREFIX);
set_val_strc(arr, 0, group_prefix); /* TEAM */
set_val_str(arr, 1, g_string_free(ports_str, FALSE));
set_val_str(arr, 2, sanitize_team_config(nm_device_team_get_config(NM_DEVICE_TEAM(device))));
set_val_str(arr, 1, g_strdup(ports_str->str));
set_val_str(arr, 2, g_string_free(ports_str, FALSE));
set_val_str(arr, 3, sanitize_team_config(nm_device_team_get_config(NM_DEVICE_TEAM(device))));
g_ptr_array_add(out.output_data, arr);
print_data_prepare_width(out.output_data);
@ -2812,20 +2818,62 @@ do_devices_delete(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const
}
}
typedef struct {
NmCli *nmc;
GSource *timeout_source;
} DeviceSetCbInfo;
static void
device_set_cb_info_finish(DeviceSetCbInfo *info)
{
nm_clear_g_source_inst(&info->timeout_source);
g_slice_free(DeviceSetCbInfo, info);
quit();
}
static gboolean
device_set_timeout_cb(gpointer user_data)
{
DeviceSetCbInfo *cb_info = user_data;
timeout_cb(cb_info->nmc);
device_set_cb_info_finish(cb_info);
return G_SOURCE_REMOVE;
}
static void
device_set_cb(GObject *object, GAsyncResult *result, gpointer user_data)
{
NMDevice *device = NM_DEVICE(object);
DeviceSetCbInfo *info = (DeviceSetCbInfo *) user_data;
NmCli *nmc = info->nmc;
gs_free_error GError *error = NULL;
/* Only 'managed' is treated asynchronously, 'autoconnect' is treated synchronously */
if (!nm_device_set_managed_finish(device, result, &error)) {
g_string_printf(nmc->return_text, _("Error: set managed failed: %s."), error->message);
nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
}
device_set_cb_info_finish(info);
}
static void
do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *argv)
{
#define DEV_SET_AUTOCONNECT 0
#define DEV_SET_MANAGED 1
NMDevice *device = NULL;
int i;
NMDevice *device = NULL;
DeviceSetCbInfo *cb_info = NULL;
int i;
struct {
int idx;
gboolean value;
} values[2] = {
[DEV_SET_AUTOCONNECT] = {-1},
[DEV_SET_MANAGED] = {-1},
};
} autoconnect_data = {-1};
struct {
int idx;
NMDeviceManaged value;
guint32 flags;
} managed_data = {-1};
gs_free_error GError *error = NULL;
next_arg(nmc, &argc, &argv, NULL);
@ -2847,49 +2895,120 @@ do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *ar
i = 0;
do {
gboolean flag;
if (argc == 1 && nmc->complete)
nmc_complete_strings(*argv, "managed", "autoconnect");
if (matches(*argv, "managed")) {
NMDeviceManaged val;
guint32 flags = 0;
gboolean val_bool;
gboolean perm = FALSE, perm_only = FALSE;
argc--;
argv++;
if (argc == 1 && nmc->complete) {
nmc_complete_strings(*argv,
"true",
"yes",
"on",
"false",
"no",
"off",
"up",
"down",
"reset",
"--permanent",
"--permanent-only");
}
if (managed_data.idx != -1) {
g_string_printf(nmc->return_text, _("Error: 'managed' can only be set once."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
}
while (argc > 0) {
/* --perm matches with --permanent, the most common,
* but --permanent-only requires a exact match */
if (nm_streq0(*argv, "--permanent-only"))
perm_only = TRUE;
else if (matches(*argv, "--permanent"))
perm = TRUE;
else
break;
argc--;
argv++;
}
if (perm_only && perm) {
g_string_printf(
nmc->return_text,
_("Error: '--permanent-only' and '--permanent' cannot be used together."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
} else if (perm) {
flags |= NM_DEVICE_MANAGED_FLAGS_RUNTIME | NM_DEVICE_MANAGED_FLAGS_PERMANENT;
} else if (perm_only) {
flags |= NM_DEVICE_MANAGED_FLAGS_PERMANENT;
} else {
/* If --permanent/--permanent-only are missing, set only the runtime flag, as this
* is how it used to work when these options didn't exist. */
flags |= NM_DEVICE_MANAGED_FLAGS_RUNTIME;
}
if (!argc) {
g_string_printf(nmc->return_text,
_("Error: '%s' argument is missing."),
*(argv - 1));
g_string_printf(nmc->return_text, _("Error: 'managed' argument is missing."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
}
if (argc == 1 && nmc->complete)
nmc_complete_bool(*argv);
if (!nmc_string_to_bool(*argv, &flag, &error)) {
g_string_printf(nmc->return_text, _("Error: 'managed': %s."), error->message);
if (matches(*argv, "up") || matches(*argv, "down")) {
flags |= NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE;
val = matches(*argv, "up") ? NM_DEVICE_MANAGED_YES : NM_DEVICE_MANAGED_NO;
} else if (matches(*argv, "reset")) {
val = NM_DEVICE_MANAGED_RESET;
} else if (nmc_string_to_bool(*argv, &val_bool, NULL)) {
val = val_bool ? NM_DEVICE_MANAGED_YES : NM_DEVICE_MANAGED_NO;
} else {
g_string_printf(
nmc->return_text,
_("Error: 'managed': '%s' is not valid, use 'yes/no/up/down/reset'."),
*argv);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
}
values[DEV_SET_MANAGED].idx = ++i;
values[DEV_SET_MANAGED].value = flag;
managed_data.idx = i++;
managed_data.value = val;
managed_data.flags = flags;
} else if (matches(*argv, "autoconnect")) {
gboolean val;
argc--;
argv++;
if (!argc) {
g_string_printf(nmc->return_text,
_("Error: '%s' argument is missing."),
*(argv - 1));
g_string_printf(nmc->return_text, _("Error: 'autoconnect' argument is missing."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
}
if (autoconnect_data.idx != -1) {
g_string_printf(nmc->return_text, _("Error: 'autoconnect' can only be set once."));
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
}
if (argc == 1 && nmc->complete)
nmc_complete_bool(*argv);
if (!nmc_string_to_bool(*argv, &flag, &error)) {
if (!nmc_string_to_bool(*argv, &val, &error)) {
g_string_printf(nmc->return_text, _("Error: 'autoconnect': %s."), error->message);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return;
}
values[DEV_SET_AUTOCONNECT].idx = ++i;
values[DEV_SET_AUTOCONNECT].value = flag;
autoconnect_data.idx = i++;
autoconnect_data.value = val;
} else {
g_string_printf(nmc->return_text, _("Error: property '%s' is not known."), *argv);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
@ -2902,15 +3021,28 @@ do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *ar
/* when multiple properties are specified, set them in the order as they
* are specified on the command line. */
if (values[DEV_SET_AUTOCONNECT].idx >= 0 && values[DEV_SET_MANAGED].idx >= 0
&& values[DEV_SET_MANAGED].idx < values[DEV_SET_AUTOCONNECT].idx) {
nm_device_set_managed(device, values[DEV_SET_MANAGED].value);
values[DEV_SET_MANAGED].idx = -1;
for (i = 0; i < 2; i++) {
if (autoconnect_data.idx == i) {
nm_device_set_autoconnect(device, autoconnect_data.value);
}
if (managed_data.idx == i) {
cb_info = g_slice_new0(DeviceSetCbInfo);
cb_info->nmc = nmc;
if (nmc->timeout > 0)
cb_info->timeout_source =
nm_g_timeout_add_seconds_source(nmc->timeout, device_set_timeout_cb, cb_info);
nmc->nowait_flag = (nmc->timeout == 0);
nmc->should_wait++;
nm_device_set_managed_async(device,
managed_data.value,
managed_data.flags,
NULL,
device_set_cb,
cb_info);
}
}
if (values[DEV_SET_AUTOCONNECT].idx >= 0)
nm_device_set_autoconnect(device, values[DEV_SET_AUTOCONNECT].value);
if (values[DEV_SET_MANAGED].idx >= 0)
nm_device_set_managed(device, values[DEV_SET_MANAGED].value);
}
static void

View file

@ -629,7 +629,7 @@
alias="type"
nmcli-description="Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, &quot;802-3-ethernet&quot; or &quot;802-11-wireless&quot; or &quot;bluetooth&quot;, etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, &quot;vpn&quot; or &quot;bridge&quot;, etc)."
format="string"
values="6lowpan, 802-11-olpc-mesh, 802-11-wireless, 802-3-ethernet, adsl, bluetooth, bond, bridge, cdma, dummy, generic, gsm, hsr, infiniband, ip-tunnel, ipvlan, loopback, macsec, macvlan, ovs-bridge, ovs-interface, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, vxlan, wifi-p2p, wimax, wireguard, wpan" />
values="6lowpan, 802-11-olpc-mesh, 802-11-wireless, 802-3-ethernet, adsl, bluetooth, bond, bridge, cdma, dummy, generic, geneve, gsm, hsr, infiniband, ip-tunnel, ipvlan, loopback, macsec, macvlan, ovs-bridge, ovs-interface, ovs-port, pppoe, team, tun, veth, vlan, vpn, vrf, vxlan, wifi-p2p, wimax, wireguard, wpan" />
<property name="interface-name"
alias="ifname"
nmcli-description="The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface."
@ -1110,6 +1110,36 @@
nmcli-description="Name of the device handler that will be invoked to add and delete the device for this connection. The name can only contain ASCII alphanumeric characters and &apos;-&apos;, &apos;_&apos;, &apos;.&apos;. It cannot start with &apos;.&apos;. See the NetworkManager-dispatcher(8) man page for more details about how to write the device handler. By setting this property the generic connection becomes &quot;virtual&quot;, meaning that it can be activated without an existing device; the device will be created at the time the connection is started by invoking the device-handler."
format="string" />
</setting>
<setting name="geneve" >
<property name="id"
alias="id"
nmcli-description="Specifies the GENEVE Network Identifier (or GENEVE Segment Identifier) to use."
format="integer"
values="0 - 16777215" />
<property name="remote"
alias="remote"
nmcli-description="Specifies the unicast destination IP address to use in outgoing packets when communicating with the remote GENEVE tunnel endpoint."
format="string" />
<property name="destination-port"
alias="destination-port"
nmcli-description="Specifies the UDP destination port to communicate to the remote GENEVE tunnel endpoint."
format="integer"
values="0 - 65535" />
<property name="tos"
nmcli-description="Specifies the TOS value to use in outgoing packets. The special value &quot;inherit&quot; (1) means inherit from outer packet."
format="integer"
values="0 - 255"
special-values="inherit (1)" />
<property name="ttl"
nmcli-description="Specifies the time-to-live value to use in outgoing packets. The special value &quot;inherit&quot; (-1) means inherit from outer packet, 0 means auto, 1-255 are fixed values."
format="integer"
values="-1 - 255"
special-values="inherit (-1)" />
<property name="df"
nmcli-description="Specifies how the Don&apos;t Fragment (DF) flag should be handled in the outer IP header of GENEVE tunnel packets. &quot;unset&quot; (0) (0): Don&apos;t set the DF flag, packets may be fragmented. &quot;set&quot; (1) (1): Always set the DF flag, packets will not be fragmented. &quot;inherit&quot; (2) (2): Inherit the DF flag from the inner IP header."
format="choice (NMSettingGeneveDf)"
values="unset (0), set (1), inherit (2)" />
</setting>
<setting name="gsm" >
<property name="auto-config"
nmcli-description="When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database."

View file

@ -38,6 +38,14 @@ _meta_type_nmc_generic_info_get_name(const NMMetaAbstractInfo *abstract_info, gb
return info->name;
}
static const char *
_meta_type_nmc_generic_info_get_alias(const NMMetaAbstractInfo *abstract_info)
{
const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) abstract_info;
return info->alias;
}
static const NMMetaAbstractInfo *const *
_meta_type_nmc_generic_info_get_nested(const NMMetaAbstractInfo *abstract_info,
guint *out_len,
@ -101,6 +109,7 @@ _meta_type_nmc_generic_info_get_fcn(const NMMetaAbstractInfo *abstract_info,
const NMMetaType nmc_meta_type_generic_info = {
.type_name = "nmc-generic-info",
.get_name = _meta_type_nmc_generic_info_get_name,
.get_alias = _meta_type_nmc_generic_info_get_alias,
.get_nested = _meta_type_nmc_generic_info_get_nested,
.get_fcn = _meta_type_nmc_generic_info_get_fcn,
};

View file

@ -157,6 +157,7 @@ typedef enum {
NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CON_PATH,
NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_ZONE,
NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CONTROLLER_PATH,
NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_MASTER_PATH,
_NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_NUM,
NMC_GENERIC_INFO_TYPE_CON_VPN_TYPE = 0,
@ -260,6 +261,7 @@ struct _NmcMetaGenericInfo {
NmcGenericInfoType info_type;
const char *name;
const char *name_header;
const char *alias;
const NmcMetaGenericInfo *const *nested;
#define NMC_META_GENERIC_INFO_GET_FCN_ARGS \

View file

@ -112,7 +112,7 @@ strings_transform_from_entry(GBinding *binding,
static gboolean
hostname_filter(NmtNewtEntry *entry, const char *text, int ch, int position, gpointer user_data)
{
return g_ascii_isalnum(ch) || ch == '.' || ch == '-';
return g_ascii_isalnum(ch) || ch == '.' || ch == '-' || ch == '~';
}
static NmtNewtWidget *

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@ SETTING_DNS_OPTION_* parent="NM.SettingDnsOption" name="SETTIN
SETTING_DUMMY_* parent="NM.SettingDummy" name="SETTING_DUMMY_(.+)"
SETTING_ETHTOOL_* parent="NM.SettingEthtool" name="SETTING_ETHTOOL_(.+)"
SETTING_GENERIC_* parent="NM.SettingGeneric" name="SETTING_GENERIC_(.+)"
SETTING_GENEVE_* parent="NM.SettingGeneve" name="SETTING_GENEVE_(.+)"
SETTING_GSM_* parent="NM.SettingGsm" name="SETTING_GSM_(.+)"
SETTING_HOSTNAME_* parent="NM.SettingHostname" name="SETTING_HOSTNAME_(.+)"
SETTING_HSR_* parent="NM.SettingHsr" name="SETTING_HSR_(.+)"
@ -114,6 +115,7 @@ DEVICE_BT_* parent="NM.DeviceBt" name="DEVICE
DEVICE_DUMMY_* parent="NM.DeviceDummy" name="DEVICE_DUMMY_(.+)"
DEVICE_ETHERNET_* parent="NM.DeviceEthernet" name="DEVICE_ETHERNET_(.+)"
DEVICE_GENERIC_* parent="NM.DeviceGeneric" name="DEVICE_GENERIC_(.+)"
DEVICE_GENEVE_* parent="NM.DeviceGeneve" name="DEVICE_GENEVE_(.+)"
DEVICE_HSR_* parent="NM.DeviceHsr" name="DEVICE_HSR_(.+)"
DEVICE_INFINIBAND_* parent="NM.DeviceInfiniband" name="DEVICE_INFINIBAND_(.+)"
DEVICE_IP_TUNNEL_* parent="NM.DeviceIPTunnel" name="DEVICE_IP_TUNNEL_(.+)"