diff --git a/Makefile.am b/Makefile.am
index 51bdca457b..3c294ab767 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1024,6 +1024,8 @@ introspection_sources = \
introspection/org.freedesktop.NetworkManager.Device.IPTunnel.h \
introspection/org.freedesktop.NetworkManager.Device.Infiniband.c \
introspection/org.freedesktop.NetworkManager.Device.Infiniband.h \
+ introspection/org.freedesktop.NetworkManager.Device.Loopback.c \
+ introspection/org.freedesktop.NetworkManager.Device.Loopback.h \
introspection/org.freedesktop.NetworkManager.Device.Lowpan.c \
introspection/org.freedesktop.NetworkManager.Device.Lowpan.h \
introspection/org.freedesktop.NetworkManager.Device.Macsec.c \
@@ -1111,6 +1113,7 @@ DBUS_INTERFACE_DOCS = \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Generic.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.IPTunnel.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Infiniband.xml \
+ docs/api/dbus-org.freedesktop.NetworkManager.Device.Loopback.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Lowpan.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Macsec.xml \
docs/api/dbus-org.freedesktop.NetworkManager.Device.Macvlan.xml \
@@ -1179,6 +1182,7 @@ dbusinterfaces_DATA = \
introspection/org.freedesktop.NetworkManager.Device.Generic.xml \
introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml \
introspection/org.freedesktop.NetworkManager.Device.Infiniband.xml \
+ introspection/org.freedesktop.NetworkManager.Device.Loopback.xml \
introspection/org.freedesktop.NetworkManager.Device.Lowpan.xml \
introspection/org.freedesktop.NetworkManager.Device.Macsec.xml \
introspection/org.freedesktop.NetworkManager.Device.Macvlan.xml \
@@ -1260,6 +1264,7 @@ src_libnm_core_impl_lib_h_pub_real = \
src/libnm-core-public/nm-setting-ip-tunnel.h \
src/libnm-core-public/nm-setting-ip4-config.h \
src/libnm-core-public/nm-setting-ip6-config.h \
+ src/libnm-core-public/nm-setting-loopback.h \
src/libnm-core-public/nm-setting-macsec.h \
src/libnm-core-public/nm-setting-macvlan.h \
src/libnm-core-public/nm-setting-match.h \
@@ -1339,6 +1344,7 @@ src_libnm_core_impl_lib_c_settings_real = \
src/libnm-core-impl/nm-setting-ip-tunnel.c \
src/libnm-core-impl/nm-setting-ip4-config.c \
src/libnm-core-impl/nm-setting-ip6-config.c \
+ src/libnm-core-impl/nm-setting-loopback.c \
src/libnm-core-impl/nm-setting-macsec.c \
src/libnm-core-impl/nm-setting-macvlan.c \
src/libnm-core-impl/nm-setting-match.c \
@@ -1732,6 +1738,7 @@ libnm_lib_h_pub_real = \
src/libnm-client-public/nm-device-generic.h \
src/libnm-client-public/nm-device-infiniband.h \
src/libnm-client-public/nm-device-ip-tunnel.h \
+ src/libnm-client-public/nm-device-loopback.h \
src/libnm-client-public/nm-device-macsec.h \
src/libnm-client-public/nm-device-macvlan.h \
src/libnm-client-public/nm-device-modem.h \
@@ -1802,6 +1809,7 @@ libnm_lib_c_real = \
src/libnm-client-impl/nm-device-generic.c \
src/libnm-client-impl/nm-device-infiniband.c \
src/libnm-client-impl/nm-device-ip-tunnel.c \
+ src/libnm-client-impl/nm-device-loopback.c \
src/libnm-client-impl/nm-device-macsec.c \
src/libnm-client-impl/nm-device-macvlan.c \
src/libnm-client-impl/nm-device-modem.c \
@@ -2618,6 +2626,8 @@ src_core_libNetworkManager_la_SOURCES = \
src/core/devices/nm-device-infiniband.h \
src/core/devices/nm-device-ip-tunnel.c \
src/core/devices/nm-device-ip-tunnel.h \
+ src/core/devices/nm-device-loopback.c \
+ src/core/devices/nm-device-loopback.h \
src/core/devices/nm-device-macsec.c \
src/core/devices/nm-device-macsec.h \
src/core/devices/nm-device-macvlan.c \
diff --git a/NEWS b/NEWS
index b4737fe8a9..8da2cf83de 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,12 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
switch.
* dns: support specifying DNS-over-TLS server name (SNI) for static
name servers. Only works with systemd-resolved plugin.
+* Add support of loopback interface and a new connection type "loopback". On D-Bus,
+ the loopback interface now has the "org.freedesktop.NetworkManager.Device.Loopback"
+ interface instead of org.freedesktop.NetworkManager.Device.Generic".
+ Loopback handling is special in that when the NetworkManager profile is
+ deactivated, the interfaces will still be up and configured with a 127.0.0.1
+ address.
=============================================
NetworkManager-1.40
diff --git a/docs/api/Makefile.am b/docs/api/Makefile.am
index 359e91fd8f..4bdce0e00b 100644
--- a/docs/api/Makefile.am
+++ b/docs/api/Makefile.am
@@ -41,6 +41,7 @@ content_files = \
dbus-org.freedesktop.NetworkManager.Device.Generic.xml \
dbus-org.freedesktop.NetworkManager.Device.IPTunnel.xml \
dbus-org.freedesktop.NetworkManager.Device.Infiniband.xml \
+ dbus-org.freedesktop.NetworkManager.Device.Loopback.xml \
dbus-org.freedesktop.NetworkManager.Device.Macsec.xml \
dbus-org.freedesktop.NetworkManager.Device.Macvlan.xml \
dbus-org.freedesktop.NetworkManager.Device.Modem.xml \
diff --git a/docs/api/network-manager-docs.xml b/docs/api/network-manager-docs.xml
index 442b05aa7d..d8f4725cf6 100644
--- a/docs/api/network-manager-docs.xml
+++ b/docs/api/network-manager-docs.xml
@@ -193,6 +193,7 @@
+
diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml
index 441a89e751..d20f01a2e2 100644
--- a/docs/libnm/libnm-docs.xml
+++ b/docs/libnm/libnm-docs.xml
@@ -332,6 +332,7 @@ print ("NetworkManager version " + client.get_version())]]>
+
@@ -381,6 +382,7 @@ print ("NetworkManager version " + client.get_version())]]>
+
diff --git a/introspection/meson.build b/introspection/meson.build
index 7d495f7af3..9a35548842 100644
--- a/introspection/meson.build
+++ b/introspection/meson.build
@@ -17,6 +17,7 @@ ifaces = [
'org.freedesktop.NetworkManager.Device.Generic',
'org.freedesktop.NetworkManager.Device.IPTunnel',
'org.freedesktop.NetworkManager.Device.Infiniband',
+ 'org.freedesktop.NetworkManager.Device.Loopback',
'org.freedesktop.NetworkManager.Device.Lowpan',
'org.freedesktop.NetworkManager.Device.Macsec',
'org.freedesktop.NetworkManager.Device.Macvlan',
diff --git a/introspection/org.freedesktop.NetworkManager.Device.Loopback.xml b/introspection/org.freedesktop.NetworkManager.Device.Loopback.xml
new file mode 100644
index 0000000000..902363317c
--- /dev/null
+++ b/introspection/org.freedesktop.NetworkManager.Device.Loopback.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml
index 301172e4cc..2c1fc85217 100644
--- a/man/NetworkManager.conf.xml
+++ b/man/NetworkManager.conf.xml
@@ -1010,6 +1010,10 @@ ipv6.ip6-privacy=0
removes extraneous routes from the tables.
+
+ loopback.mtu
+ If configured explicitly to 0, the MTU is not reconfigured during device activation unless it is required due to IPv6 constraints. If left unspecified, a DHCP/IPv6 SLAAC provided value is used or the MTU is left unspecified on activation.
+
sriov.autoprobe-drivers
If left unspecified, drivers are autoprobed when the SR-IOV VF gets created.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a1a5730075..a0a3045029 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -13,6 +13,7 @@ src/core/devices/nm-device-ethernet-utils.c
src/core/devices/nm-device-ethernet.c
src/core/devices/nm-device-infiniband.c
src/core/devices/nm-device-ip-tunnel.c
+src/core/devices/nm-device-loopback.c
src/core/devices/nm-device-macvlan.c
src/core/devices/nm-device-tun.c
src/core/devices/nm-device-veth.c
@@ -47,6 +48,7 @@ src/libnm-client-impl/nm-device-ethernet.c
src/libnm-client-impl/nm-device-generic.c
src/libnm-client-impl/nm-device-infiniband.c
src/libnm-client-impl/nm-device-ip-tunnel.c
+src/libnm-client-impl/nm-device-loopback.c
src/libnm-client-impl/nm-device-macvlan.c
src/libnm-client-impl/nm-device-modem.c
src/libnm-client-impl/nm-device-olpc-mesh.c
diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c
index 41e9480fe5..2f0ae2e8e5 100644
--- a/src/core/devices/nm-device-factory.c
+++ b/src/core/devices/nm-device-factory.c
@@ -398,6 +398,7 @@ nm_device_factory_manager_load_factories(NMDeviceFactoryManagerFactoryFunc callb
_ADD_INTERNAL(nm_ethernet_device_factory_get_type);
_ADD_INTERNAL(nm_infiniband_device_factory_get_type);
_ADD_INTERNAL(nm_ip_tunnel_device_factory_get_type);
+ _ADD_INTERNAL(nm_loopback_device_factory_get_type);
_ADD_INTERNAL(nm_macsec_device_factory_get_type);
_ADD_INTERNAL(nm_macvlan_device_factory_get_type);
_ADD_INTERNAL(nm_ppp_device_factory_get_type);
diff --git a/src/core/devices/nm-device-loopback.c b/src/core/devices/nm-device-loopback.c
new file mode 100644
index 0000000000..129cbb8a85
--- /dev/null
+++ b/src/core/devices/nm-device-loopback.c
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+#include "nm-device-loopback.h"
+#include "nm-device-private.h"
+#include "libnm-platform/nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-setting-loopback.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
+
+#define _NMLOG_DEVICE_TYPE NMDeviceLoopback
+#include "nm-device-logging.h"
+
+/*****************************************************************************/
+
+struct _NMDeviceLoopback {
+ NMDevice parent;
+};
+
+struct _NMDeviceLoopbackClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE(NMDeviceLoopback, nm_device_loopback, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static NMDeviceCapabilities
+get_generic_capabilities(NMDevice *dev)
+{
+ /* In many aspects, loopback is a software device. Still, don't return
+ * NM_DEVICE_CAP_IS_SOFTWARE here, because we cannot delete nor create
+ * such an interface. */
+ return NM_DEVICE_CAP_NONE;
+}
+
+static guint32
+get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force)
+{
+ return nm_device_get_configured_mtu_from_connection(device,
+ NM_TYPE_SETTING_LOOPBACK,
+ out_source);
+}
+
+static const char *
+get_auto_ip_config_method(NMDevice *device, int addr_family)
+{
+ return NM_IS_IPv4(addr_family) ? NM_SETTING_IP4_CONFIG_METHOD_MANUAL
+ : NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+}
+
+static gboolean
+complete_connection(NMDevice *device,
+ NMConnection *connection,
+ const char *specific_object,
+ NMConnection *const *existing_connections,
+ GError **error)
+{
+ nm_utils_complete_generic_with_params(nm_device_get_platform(device),
+ connection,
+ NM_SETTING_LOOPBACK_SETTING_NAME,
+ existing_connections,
+ NULL,
+ _("Loopback connection"),
+ NULL,
+ nm_device_get_ip_iface(device));
+
+ _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_LOOPBACK);
+
+ return TRUE;
+}
+
+static void
+update_connection(NMDevice *device, NMConnection *connection)
+{
+ _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_LOOPBACK);
+}
+
+static gboolean
+can_reapply_change(NMDevice *device,
+ const char *setting_name,
+ NMSetting *s_old,
+ NMSetting *s_new,
+ GHashTable *diffs,
+ GError **error)
+{
+ if (!nm_streq(setting_name, NM_SETTING_LOOPBACK_SETTING_NAME)) {
+ return NM_DEVICE_CLASS(nm_device_loopback_parent_class)
+ ->can_reapply_change(device, setting_name, s_old, s_new, diffs, error);
+ }
+
+ return nm_device_hash_check_invalid_keys(diffs,
+ NM_SETTING_LOOPBACK_SETTING_NAME,
+ error,
+ NM_SETTING_LOOPBACK_MTU);
+}
+
+/*****************************************************************************/
+
+static const NMDBusInterfaceInfoExtended interface_info_device_loopback = {
+ .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(NM_DBUS_INTERFACE_DEVICE_LOOPBACK, ),
+};
+
+static void
+nm_device_loopback_init(NMDeviceLoopback *self)
+{}
+
+static void
+nm_device_loopback_class_init(NMDeviceLoopbackClass *klass)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS(klass);
+ NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
+
+ dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_loopback);
+
+ device_class->connection_type_supported = NM_SETTING_LOOPBACK_SETTING_NAME;
+ device_class->connection_type_check_compatible = NM_SETTING_LOOPBACK_SETTING_NAME;
+ device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_LOOPBACK);
+
+ device_class->complete_connection = complete_connection;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->update_connection = update_connection;
+ device_class->act_stage1_prepare_set_hwaddr_ethernet = TRUE;
+ device_class->get_auto_ip_config_method = get_auto_ip_config_method;
+ device_class->get_configured_mtu = get_configured_mtu;
+ device_class->can_reapply_change = can_reapply_change;
+}
+
+/*****************************************************************************/
+
+#define NM_TYPE_LOOPBACK_DEVICE_FACTORY (nm_loopback_device_factory_get_type())
+#define NM_LOOPBACK_DEVICE_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LOOPBACK_DEVICE_FACTORY, NMLoopbackDeviceFactory))
+
+static NMDevice *
+create_device(NMDeviceFactory *factory,
+ const char *iface,
+ const NMPlatformLink *plink,
+ NMConnection *connection,
+ gboolean *out_ignore)
+{
+ return g_object_new(NM_TYPE_DEVICE_LOOPBACK,
+ NM_DEVICE_IFACE,
+ iface,
+ NM_DEVICE_TYPE_DESC,
+ "Loopback",
+ NM_DEVICE_DEVICE_TYPE,
+ NM_DEVICE_TYPE_LOOPBACK,
+ NM_DEVICE_LINK_TYPE,
+ NM_LINK_TYPE_LOOPBACK,
+ NULL);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL(
+ LOOPBACK,
+ Loopback,
+ loopback,
+ NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(NM_LINK_TYPE_LOOPBACK)
+ NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(NM_SETTING_LOOPBACK_SETTING_NAME),
+ factory_class->create_device = create_device;);
diff --git a/src/core/devices/nm-device-loopback.h b/src/core/devices/nm-device-loopback.h
new file mode 100644
index 0000000000..42db8ec050
--- /dev/null
+++ b/src/core/devices/nm-device-loopback.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_LOOPBACK_H__
+#define __NETWORKMANAGER_DEVICE_LOOPBACK_H__
+
+#include "nm-device-generic.h"
+
+#define NM_TYPE_DEVICE_LOOPBACK (nm_device_loopback_get_type())
+#define NM_DEVICE_LOOPBACK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopback))
+#define NM_DEVICE_LOOPBACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass))
+#define NM_IS_DEVICE_LOOPBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_LOOPBACK))
+#define NM_IS_DEVICE_LOOPBACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_LOOPBACK))
+#define NM_DEVICE_LOOPBACK_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass))
+
+typedef struct _NMDeviceLoopback NMDeviceLoopback;
+typedef struct _NMDeviceLoopbackClass NMDeviceLoopbackClass;
+
+GType nm_device_loopback_get_type(void);
+
+#endif /* __NETWORKMANAGER_DEVICE_LOOPBACK_H__ */
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 7783a82e68..a3f82ba6e2 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -77,6 +77,7 @@
#include "nm-device-generic.h"
#include "nm-device-bridge.h"
+#include "nm-device-loopback.h"
#include "nm-device-vlan.h"
#include "nm-device-vrf.h"
#include "nm-device-wireguard.h"
@@ -1588,6 +1589,9 @@ _prop_get_ipv4_link_local(NMDevice *self)
if (!s_ip4)
return NM_SETTING_IP4_LL_DISABLED;
+ if (NM_IS_DEVICE_LOOPBACK(self))
+ return NM_SETTING_IP4_LL_DISABLED;
+
link_local = nm_setting_ip4_config_get_link_local(s_ip4);
if (link_local == NM_SETTING_IP4_LL_DEFAULT) {
@@ -2741,13 +2745,6 @@ _ethtool_state_set(NMDevice *self)
/*****************************************************************************/
-static gboolean
-is_loopback(NMDevice *self)
-{
- return NM_IS_DEVICE_GENERIC(self)
- && NM_DEVICE_GET_PRIVATE(self)->ifindex == NM_LOOPBACK_IFINDEX;
-}
-
gboolean
nm_device_is_vpn(NMDevice *self)
{
@@ -5019,6 +5016,9 @@ nm_device_get_route_metric_default(NMDeviceType device_type)
*/
switch (device_type) {
+ case NM_DEVICE_TYPE_LOOPBACK:
+ return 30;
+
/* 50 is also used for VPN plugins (NM_VPN_ROUTE_METRIC_DEFAULT).
*
* Note that returning 50 from this function means that this device-type is
@@ -5439,7 +5439,7 @@ concheck_is_possible(NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
- if (!nm_device_is_real(self) || is_loopback(self))
+ if (!nm_device_is_real(self) || NM_IS_DEVICE_LOOPBACK(self))
return FALSE;
/* we enable periodic checks for every device state (except UNKNOWN). Especially with
@@ -7485,11 +7485,6 @@ realize_start_setup(NMDevice *self,
NM_UNMANAGED_EXTERNAL_DOWN,
_dev_unmanaged_is_external_down(self, TRUE));
- /* Unmanaged the loopback device with an explicit NM_UNMANAGED_BY_TYPE flag.
- * Later we might want to manage 'lo' too. Currently, that doesn't work because
- * NetworkManager might down the interface or remove the 127.0.0.1 address. */
- nm_device_set_unmanaged_flags(self, NM_UNMANAGED_BY_TYPE, is_loopback(self));
-
nm_device_set_unmanaged_by_user_udev(self);
nm_device_set_unmanaged_by_user_conf(self);
@@ -7679,9 +7674,8 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error)
nm_device_set_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
nm_device_set_unmanaged_flags(self,
- NM_UNMANAGED_BY_TYPE | NM_UNMANAGED_USER_UDEV
- | NM_UNMANAGED_USER_EXPLICIT | NM_UNMANAGED_EXTERNAL_DOWN
- | NM_UNMANAGED_IS_SLAVE,
+ NM_UNMANAGED_USER_UDEV | NM_UNMANAGED_USER_EXPLICIT
+ | NM_UNMANAGED_EXTERNAL_DOWN | NM_UNMANAGED_IS_SLAVE,
NM_UNMAN_FLAG_OP_FORGET);
nm_device_state_changed(self,
@@ -10839,7 +10833,8 @@ _dev_ipll6_set_llstate(NMDevice *self, NML3IPv6LLState llstate, const struct in6
|| (!priv->ipll_data_6.v6.ipv6ll
&& NM_IN_SET(priv->ipll_data_6.v6.llstate,
NM_L3_IPV6LL_STATE_NONE,
- NM_L3_IPV6LL_STATE_DEFUNCT)));
+ NM_L3_IPV6LL_STATE_DEFUNCT,
+ NM_L3_IPV6LL_STATE_READY)));
switch (priv->ipll_data_6.v6.llstate) {
case NM_L3_IPV6LL_STATE_NONE:
@@ -10922,6 +10917,11 @@ _dev_ipll6_start(NMDevice *self)
if (priv->ipll_data_6.v6.ipv6ll)
return;
+ if (NM_IS_DEVICE_LOOPBACK(self)) {
+ _dev_ipll6_set_llstate(self, NM_L3_IPV6LL_STATE_READY, NULL);
+ return;
+ }
+
if (!priv->l3cfg) {
_LOGD(LOGD_IP6, "linklocal6: no IP link for IPv6");
goto out_fail;
@@ -11033,6 +11033,10 @@ nm_device_get_configured_mtu_from_connection(NMDevice *self,
if (setting)
mtu = nm_setting_wireguard_get_mtu(NM_SETTING_WIREGUARD(setting));
global_property_name = NM_CON_DEFAULT("wireguard.mtu");
+ } else if (setting_type == NM_TYPE_SETTING_LOOPBACK) {
+ if (setting)
+ mtu = nm_setting_loopback_get_mtu(NM_SETTING_LOOPBACK(setting));
+ global_property_name = NM_CON_DEFAULT("loopback.mtu");
} else
g_return_val_if_reached(0);
@@ -14169,7 +14173,6 @@ NM_UTILS_FLAGS2STR_DEFINE(nm_unmanaged_flags2str,
NMUnmanagedFlags,
NM_UTILS_FLAGS2STR(NM_UNMANAGED_SLEEPING, "sleeping"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_QUITTING, "quitting"),
- NM_UTILS_FLAGS2STR(NM_UNMANAGED_BY_TYPE, "by-type"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_PLATFORM_INIT, "platform-init"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_USER_EXPLICIT, "user-explicit"),
NM_UTILS_FLAGS2STR(NM_UNMANAGED_BY_DEFAULT, "by-default"),
@@ -17212,10 +17215,12 @@ get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family)
if (head_entry) {
c_list_for_each_entry (iter, &head_entry->lst_entries_head, lst_entries) {
- const NMPlatformIPAddress *addr = NMP_OBJECT_CAST_IP_ADDRESS(iter->obj);
+ const NMPlatformIPXAddress *addr = NMP_OBJECT_CAST_IPX_ADDRESS(iter->obj);
if (IS_IPv4) {
- return g_inet_address_new_from_bytes(addr->address_ptr, G_SOCKET_FAMILY_IPV4);
+ if (nm_ip4_addr_is_loopback(addr->a4.address))
+ continue;
+ return g_inet_address_new_from_bytes(addr->ax.address_ptr, G_SOCKET_FAMILY_IPV4);
}
/* For IPv6 prefer, in order:
@@ -17224,15 +17229,19 @@ get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family)
* - link-local
*/
- if (!IN6_IS_ADDR_LINKLOCAL(addr->address_ptr)) {
- if (!(addr->n_ifa_flags & IFA_F_DEPRECATED)) {
- return g_inet_address_new_from_bytes(addr->address_ptr, G_SOCKET_FAMILY_IPV6);
+ if (IN6_ARE_ADDR_EQUAL(&addr->a6.address, &in6addr_loopback))
+ continue;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(addr->ax.address_ptr)) {
+ if (!(addr->ax.n_ifa_flags & IFA_F_DEPRECATED)) {
+ return g_inet_address_new_from_bytes(addr->ax.address_ptr,
+ G_SOCKET_FAMILY_IPV6);
}
- addr6_nonll = addr->address_ptr;
+ addr6_nonll = addr->ax.address_ptr;
continue;
}
- addr6_ll = addr->address_ptr;
+ addr6_ll = addr->ax.address_ptr;
}
if (addr6_nonll || addr6_ll)
@@ -17639,7 +17648,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_VRF);
+ nm_assert(priv->type <= NM_DEVICE_TYPE_LOOPBACK);
break;
case PROP_LINK_TYPE:
/* construct-only */
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index 3dfa572064..cd898085be 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -566,7 +566,6 @@ void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device);
* @NM_UNMANAGED_NONE: placeholder value
* @NM_UNMANAGED_SLEEPING: %TRUE when unmanaged because NM is sleeping.
* @NM_UNMANAGED_QUITTING: %TRUE when unmanaged because NM is shutting down.
- * @NM_UNMANAGED_BY_TYPE: %TRUE for unmanaging device by type, like loopback.
* @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not
* yet initialized. Unrealized device are also unmanaged for this reason.
* @NM_UNMANAGED_USER_EXPLICIT: %TRUE when unmanaged by explicit user decision
@@ -596,20 +595,19 @@ typedef enum {
* the device cannot be managed. */
NM_UNMANAGED_SLEEPING = (1LL << 0),
NM_UNMANAGED_QUITTING = (1LL << 1),
- NM_UNMANAGED_BY_TYPE = (1LL << 2),
- NM_UNMANAGED_PLATFORM_INIT = (1LL << 3),
- NM_UNMANAGED_USER_EXPLICIT = (1LL << 4),
- NM_UNMANAGED_USER_SETTINGS = (1LL << 5),
+ NM_UNMANAGED_PLATFORM_INIT = (1LL << 2),
+ NM_UNMANAGED_USER_EXPLICIT = (1LL << 3),
+ NM_UNMANAGED_USER_SETTINGS = (1LL << 4),
/* These flags can be non-effective and be overwritten
* by other flags. */
- NM_UNMANAGED_BY_DEFAULT = (1LL << 6),
- NM_UNMANAGED_USER_CONF = (1LL << 7),
- NM_UNMANAGED_USER_UDEV = (1LL << 8),
- NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 9),
- NM_UNMANAGED_IS_SLAVE = (1LL << 10),
+ NM_UNMANAGED_BY_DEFAULT = (1LL << 5),
+ NM_UNMANAGED_USER_CONF = (1LL << 6),
+ NM_UNMANAGED_USER_UDEV = (1LL << 7),
+ NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 8),
+ NM_UNMANAGED_IS_SLAVE = (1LL << 9),
- NM_UNMANAGED_ALL = ((1LL << 11) - 1),
+ NM_UNMANAGED_ALL = ((1LL << 10) - 1),
} NMUnmanagedFlags;
typedef enum {
diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c
index 96e2794c6a..6bdc8460a8 100644
--- a/src/core/dns/nm-dns-systemd-resolved.c
+++ b/src/core/dns/nm-dns-systemd-resolved.c
@@ -477,6 +477,8 @@ prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic)
guint i;
gboolean require_dns_ex = FALSE;
+ nm_assert(ic->ifindex != NM_LOOPBACK_IFINDEX);
+
g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))"));
g_variant_builder_add(&dns, "i", ic->ifindex);
g_variant_builder_open(&dns, G_VARIANT_TYPE("a(iay)"));
@@ -791,6 +793,13 @@ update(NMDnsPlugin *plugin,
nm_assert(ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd));
+ if (ifindex == NM_LOOPBACK_IFINDEX) {
+ /* systemd-resolved API is per-link, and loopback is not supported.
+ * Unclear what to do about DNS configuration on loopback. Just skip
+ * it here. */
+ continue;
+ }
+
ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex));
if (!ic) {
ic = g_slice_new(InterfaceConfig);
diff --git a/src/core/meson.build b/src/core/meson.build
index 523a51ca94..6c1d463ed1 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -104,6 +104,7 @@ libNetworkManager = static_library(
'devices/nm-device-generic.c',
'devices/nm-device-infiniband.c',
'devices/nm-device-ip-tunnel.c',
+ 'devices/nm-device-loopback.c',
'devices/nm-device-macsec.c',
'devices/nm-device-macvlan.c',
'devices/nm-device-ppp.c',
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index d69f28b64c..bd13bda4ab 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -1430,7 +1430,8 @@ _acd_data_free(AcdData *acd_data)
}
static guint
-_acd_data_collect_tracks_data(const AcdData *acd_data,
+_acd_data_collect_tracks_data(NML3Cfg *self,
+ const AcdData *acd_data,
NMTernary dirty_selector,
guint32 *out_best_acd_timeout_msec,
NML3AcdDefendType *out_best_acd_defend_type)
@@ -1466,6 +1467,12 @@ _acd_data_collect_tracks_data(const AcdData *acd_data,
nm_assert(n == 0 || best_acd_defend_type > _NM_L3_ACD_DEFEND_TYPE_NONE);
nm_assert(best_acd_defend_type <= NM_L3_ACD_DEFEND_TYPE_ALWAYS);
+ if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) {
+ /* On loopback interface, ACD makes no sense. We always force the
+ * timeout to zero, which means no ACD. */
+ best_acd_timeout_msec = 0;
+ }
+
NM_SET_OUT(out_best_acd_timeout_msec, n > 0 ? best_acd_timeout_msec : 0u);
NM_SET_OUT(out_best_acd_defend_type, best_acd_defend_type);
return n;
@@ -2313,7 +2320,8 @@ _l3_acd_data_state_change(NML3Cfg *self,
return;
handle_init:
- if (_acd_data_collect_tracks_data(acd_data,
+ if (_acd_data_collect_tracks_data(self,
+ acd_data,
NM_TERNARY_FALSE,
&acd_timeout_msec,
&acd_defend_type)
@@ -2362,7 +2370,8 @@ handle_init:
/* we just did a commit of the IP configuration and now visit all ACD states
* and kick off the necessary actions... */
- if (_acd_data_collect_tracks_data(acd_data,
+ if (_acd_data_collect_tracks_data(self,
+ acd_data,
NM_TERNARY_TRUE,
&acd_timeout_msec,
&acd_defend_type)
@@ -2465,7 +2474,8 @@ handle_init:
/* after a timeout, re-probe the address. This only happens if the caller
* does not deconfigure the address after USED/CONFLICT. But in that case,
* we eventually want to retry. */
- if (_acd_data_collect_tracks_data(acd_data,
+ if (_acd_data_collect_tracks_data(self,
+ acd_data,
NM_TERNARY_TRUE,
&acd_timeout_msec,
&acd_defend_type)
@@ -3748,6 +3758,38 @@ _l3cfg_update_combined_config(NML3Cfg *self,
&hook_data);
}
+ if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) {
+ NMPlatformIPXAddress ax;
+ NMPlatformIPXRoute rx;
+
+ if (!nm_l3_config_data_lookup_address_4(l3cd,
+ NM_IPV4LO_ADDR1,
+ NM_IPV4LO_PREFIXLEN,
+ NM_IPV4LO_ADDR1)) {
+ nm_l3_config_data_add_address_4(
+ l3cd,
+ nm_platform_ip4_address_init_loopback_addr1(&ax.a4));
+ }
+ if (!nm_l3_config_data_lookup_address_6(l3cd, &in6addr_loopback)) {
+ nm_l3_config_data_add_address_6(l3cd,
+ nm_platform_ip6_address_init_loopback(&ax.a6));
+ }
+
+ rx.r4 = (NMPlatformIP4Route){
+ .ifindex = NM_LOOPBACK_IFINDEX,
+ .rt_source = NM_IP_CONFIG_SOURCE_KERNEL,
+ .network = NM_IPV4LO_ADDR1,
+ .plen = NM_IPV4LO_PREFIXLEN,
+ .table_coerced = nm_platform_route_table_coerce(RT_TABLE_LOCAL),
+ .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST),
+ .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL),
+ .pref_src = NM_IPV4LO_ADDR1,
+ };
+ nm_platform_ip_route_normalize(AF_INET, &rx.rx);
+ if (!nm_l3_config_data_lookup_route(l3cd, AF_INET, &rx.rx)) {
+ nm_l3_config_data_add_route_4(l3cd, &rx.r4);
+ }
+ }
for (i = 0; i < l3_config_datas_len; i++) {
const L3ConfigData *l3cd_data = l3_config_datas_arr[i];
int IS_IPv4;
@@ -4587,6 +4629,23 @@ _l3_commit_one(NML3Cfg *self,
}
}
+ if (self->priv.ifindex == NM_LOOPBACK_IFINDEX) {
+ if (!addresses) {
+ NMPlatformIPXAddress ax;
+
+ addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
+ if (IS_IPv4) {
+ g_ptr_array_add(
+ addresses,
+ nmp_object_new(NMP_OBJECT_TYPE_IP4_ADDRESS,
+ nm_platform_ip4_address_init_loopback_addr1(&ax.a4)));
+ } else {
+ g_ptr_array_add(addresses,
+ nmp_object_new(NMP_OBJECT_TYPE_IP6_ADDRESS,
+ nm_platform_ip6_address_init_loopback(&ax.a6)));
+ }
+ }
+ }
/* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_ndisc_*(). */
/* FIXME(l3cfg): need to honor and set nm_l3_config_data_get_mtu(). */
@@ -4595,7 +4654,9 @@ _l3_commit_one(NML3Cfg *self,
self->priv.ifindex,
addresses,
addresses_prune,
- NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE);
+ self->priv.ifindex == NM_LOOPBACK_IFINDEX
+ ? NMP_IP_ADDRESS_SYNC_FLAGS_NONE
+ : NMP_IP_ADDRESS_SYNC_FLAGS_WITH_NOPREFIXROUTE);
_nodev_routes_sync(self, addr_family, commit_type, routes_nodev);
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index 7c04e8e4e5..c71e649c15 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -19,6 +19,7 @@
#include "NetworkManagerUtils.h"
#include "devices/nm-device-factory.h"
#include "devices/nm-device-generic.h"
+#include "devices/nm-device-loopback.h"
#include "devices/nm-device.h"
#include "dns/nm-dns-manager.h"
#include "dhcp/nm-dhcp-manager.h"
@@ -1461,8 +1462,11 @@ find_best_device_state(NMManager *manager)
NMActiveConnection *ac;
c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
- NMActiveConnectionState ac_state = nm_active_connection_get_state(ac);
+ NMActiveConnectionState ac_state;
+ if (NM_IS_DEVICE_LOOPBACK(nm_active_connection_get_device(ac)))
+ continue;
+ ac_state = nm_active_connection_get_state(ac);
switch (ac_state) {
case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
if (nm_active_connection_get_default(ac, AF_UNSPEC)) {
@@ -3340,6 +3344,9 @@ _get_best_connectivity(NMManager *self, int addr_family)
NMConnectivityState state;
gint64 metric;
+ if (NM_IS_DEVICE_LOOPBACK(dev))
+ continue;
+
r = nm_device_get_best_default_route(dev, addr_family);
if (r)
metric = NMP_OBJECT_CAST_IP_ROUTE(r)->metric;
@@ -7087,10 +7094,6 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde
ifindex = nm_device_get_ip_ifindex(device);
if (ifindex <= 0)
return FALSE;
- if (ifindex == NM_LOOPBACK_IFINDEX) {
- /* ignore loopback */
- return FALSE;
- }
if (!nm_platform_link_get(priv->platform, ifindex))
return FALSE;
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index 6d706dff4a..42cb4e752e 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -1883,6 +1883,12 @@ libnm_1_42_0 {
global:
nm_client_wait_shutdown;
nm_client_wait_shutdown_finish;
+ nm_device_loopback_get_type;
+ nm_setting_ip_config_get_dhcp_iaid;
+ nm_setting_ip_config_get_dhcp_iaid;
+ nm_setting_loopback_get_mtu;
+ nm_setting_loopback_get_type;
+ nm_setting_loopback_new;
nm_setting_ovs_interface_get_ofport_request;
nm_utils_ensure_gtypes;
} libnm_1_40_0;
diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build
index d72ed545b4..143126c51a 100644
--- a/src/libnm-client-impl/meson.build
+++ b/src/libnm-client-impl/meson.build
@@ -17,6 +17,7 @@ libnm_client_impl_sources = files(
'nm-device-generic.c',
'nm-device-infiniband.c',
'nm-device-ip-tunnel.c',
+ 'nm-device-loopback.c',
'nm-device-macsec.c',
'nm-device-macvlan.c',
'nm-device-modem.c',
diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c
index 7935b4bb81..57d3607cf5 100644
--- a/src/libnm-client-impl/nm-client.c
+++ b/src/libnm-client-impl/nm-client.c
@@ -31,6 +31,7 @@
#include "nm-device-generic.h"
#include "nm-device-infiniband.h"
#include "nm-device-ip-tunnel.h"
+#include "nm-device-loopback.h"
#include "nm-device-macsec.h"
#include "nm-device-macvlan.h"
#include "nm-device-modem.h"
diff --git a/src/libnm-client-impl/nm-device-loopback.c b/src/libnm-client-impl/nm-device-loopback.c
new file mode 100644
index 0000000000..9163f38dfe
--- /dev/null
+++ b/src/libnm-client-impl/nm-device-loopback.c
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#include "libnm-client-impl/nm-default-libnm.h"
+
+#include "nm-device-loopback.h"
+
+#include "nm-object-private.h"
+#include "nm-setting-loopback.h"
+#include "nm-setting-connection.h"
+
+/*****************************************************************************/
+
+struct _NMDeviceLoopback {
+ NMDevice parent;
+};
+
+struct _NMDeviceLoopbackClass {
+ NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE(NMDeviceLoopback, nm_device_loopback, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static gboolean
+connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
+{
+ const char *iface_name;
+
+ if (!NM_DEVICE_CLASS(nm_device_loopback_parent_class)
+ ->connection_compatible(device, connection, error))
+ return FALSE;
+
+ if (!nm_connection_is_type(connection, NM_SETTING_LOOPBACK_SETTING_NAME)) {
+ g_set_error_literal(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+ _("The connection was not a loopback connection."));
+ return FALSE;
+ }
+
+ iface_name = nm_connection_get_interface_name(connection);
+ if (!iface_name) {
+ g_set_error_literal(error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ _("The connection did not specify an interface name."));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GType
+get_setting_type(NMDevice *device)
+{
+ return NM_TYPE_SETTING_LOOPBACK;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_loopback_init(NMDeviceLoopback *device)
+{}
+
+const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_loopback =
+ NML_DBUS_META_IFACE_INIT(NM_DBUS_INTERFACE_DEVICE_LOOPBACK,
+ nm_device_loopback_get_type,
+ NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30, );
+
+static void
+nm_device_loopback_class_init(NMDeviceLoopbackClass *loopback_class)
+{
+ NMDeviceClass *device_class = NM_DEVICE_CLASS(loopback_class);
+
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+}
diff --git a/src/libnm-client-impl/nm-device.c b/src/libnm-client-impl/nm-device.c
index 238e7c1709..fcf07a6b52 100644
--- a/src/libnm-client-impl/nm-device.c
+++ b/src/libnm-client-impl/nm-device.c
@@ -312,6 +312,7 @@ coerce_type(NMDeviceType type)
case NM_DEVICE_TYPE_WIREGUARD:
case NM_DEVICE_TYPE_WIFI_P2P:
case NM_DEVICE_TYPE_VRF:
+ case NM_DEVICE_TYPE_LOOPBACK:
return type;
}
return NM_DEVICE_TYPE_UNKNOWN;
@@ -1811,6 +1812,8 @@ get_type_name(NMDevice *device)
return _("Wi-Fi P2P");
case NM_DEVICE_TYPE_VRF:
return _("VRF");
+ case NM_DEVICE_TYPE_LOOPBACK:
+ return _("Loopback");
case NM_DEVICE_TYPE_GENERIC:
case NM_DEVICE_TYPE_UNUSED1:
case NM_DEVICE_TYPE_UNUSED2:
diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c
index f773c7f81d..9d51226347 100644
--- a/src/libnm-client-impl/nm-libnm-utils.c
+++ b/src/libnm-client-impl/nm-libnm-utils.c
@@ -693,6 +693,7 @@ const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = {
&_nml_dbus_meta_iface_nm_device_generic,
&_nml_dbus_meta_iface_nm_device_iptunnel,
&_nml_dbus_meta_iface_nm_device_infiniband,
+ &_nml_dbus_meta_iface_nm_device_loopback,
&_nml_dbus_meta_iface_nm_device_lowpan,
&_nml_dbus_meta_iface_nm_device_macsec,
&_nml_dbus_meta_iface_nm_device_macvlan,
diff --git a/src/libnm-client-impl/nm-libnm-utils.h b/src/libnm-client-impl/nm-libnm-utils.h
index da13c615cb..a60706bdd7 100644
--- a/src/libnm-client-impl/nm-libnm-utils.h
+++ b/src/libnm-client-impl/nm-libnm-utils.h
@@ -565,7 +565,7 @@ struct _NMLDBusMetaIface {
NML_DBUS_META_IFACE_OBJ_PROPERTIES(), \
##__VA_ARGS__)
-extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[44];
+extern const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[45];
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint;
@@ -581,6 +581,7 @@ 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_infiniband;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_iptunnel;
+extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_loopback;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_lowpan;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macsec;
extern const NMLDBusMetaIface _nml_dbus_meta_iface_nm_device_macvlan;
diff --git a/src/libnm-client-public/NetworkManager.h b/src/libnm-client-public/NetworkManager.h
index cae8596662..da661db93b 100644
--- a/src/libnm-client-public/NetworkManager.h
+++ b/src/libnm-client-public/NetworkManager.h
@@ -44,6 +44,7 @@
#include "nm-setting-ip6-config.h"
#include "nm-setting-ip-config.h"
#include "nm-setting-ip-tunnel.h"
+#include "nm-setting-loopback.h"
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-match.h"
@@ -111,6 +112,7 @@
#include "nm-device-generic.h"
#include "nm-device-infiniband.h"
#include "nm-device-ip-tunnel.h"
+#include "nm-device-loopback.h"
#include "nm-device-macsec.h"
#include "nm-device-macvlan.h"
#include "nm-device-modem.h"
diff --git a/src/libnm-client-public/meson.build b/src/libnm-client-public/meson.build
index 5232bcb486..bac7e4574b 100644
--- a/src/libnm-client-public/meson.build
+++ b/src/libnm-client-public/meson.build
@@ -20,6 +20,7 @@ libnm_client_headers = files(
'nm-device-generic.h',
'nm-device-infiniband.h',
'nm-device-ip-tunnel.h',
+ 'nm-device-loopback.h',
'nm-device-macsec.h',
'nm-device-macvlan.h',
'nm-device-modem.h',
diff --git a/src/libnm-client-public/nm-autoptr.h b/src/libnm-client-public/nm-autoptr.h
index 6c34946ca7..31ae2cce26 100644
--- a/src/libnm-client-public/nm-autoptr.h
+++ b/src/libnm-client-public/nm-autoptr.h
@@ -43,6 +43,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceEthernet, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceGeneric, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceIPTunnel, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceInfiniband, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceLoopback, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceMacsec, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceMacvlan, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMDeviceModem, g_object_unref)
@@ -82,6 +83,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIP6Config, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIPConfig, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingIPTunnel, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingInfiniband, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingLoopback, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMacsec, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMacvlan, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NMSettingMatch, g_object_unref)
diff --git a/src/libnm-client-public/nm-device-loopback.h b/src/libnm-client-public/nm-device-loopback.h
new file mode 100644
index 0000000000..ae5dcc47cd
--- /dev/null
+++ b/src/libnm-client-public/nm-device-loopback.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#ifndef __NM_DEVICE_LOOPBACK_H__
+#define __NM_DEVICE_LOOPBACK_H__
+
+#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_LOOPBACK (nm_device_loopback_get_type())
+#define NM_DEVICE_LOOPBACK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopback))
+#define NM_DEVICE_LOOPBACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass))
+#define NM_IS_DEVICE_LOOPBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DEVICE_LOOPBACK))
+#define NM_IS_DEVICE_LOOPBACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DEVICE_LOOPBACK))
+#define NM_DEVICE_LOOPBACK_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DEVICE_LOOPBACK, NMDeviceLoopbackClass))
+
+/**
+ * NMDeviceLoopback:
+ *
+ * Since: 1.42
+ */
+typedef struct _NMDeviceLoopback NMDeviceLoopback;
+typedef struct _NMDeviceLoopbackClass NMDeviceLoopbackClass;
+
+NM_AVAILABLE_IN_1_42
+GType nm_device_loopback_get_type(void);
+
+G_END_DECLS
+
+#endif /* __NM_DEVICE_LOOPBACK_H__ */
diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
index 77bbdcca53..42fd7c4773 100644
--- a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
+++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
@@ -1721,6 +1721,14 @@
gprop-type="gchararray"
/>
+
+
+
diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build
index 25d4c6b859..f1bd2a081c 100644
--- a/src/libnm-core-impl/meson.build
+++ b/src/libnm-core-impl/meson.build
@@ -24,6 +24,7 @@ libnm_core_settings_sources = files(
'nm-setting-ip-tunnel.c',
'nm-setting-ip4-config.c',
'nm-setting-ip6-config.c',
+ 'nm-setting-loopback.c',
'nm-setting-macsec.c',
'nm-setting-macvlan.c',
'nm-setting-match.c',
diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c
index 5e627cfc7d..e93a1555dc 100644
--- a/src/libnm-core-impl/nm-meta-setting-base-impl.c
+++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c
@@ -34,6 +34,7 @@
#include "nm-setting-ip-tunnel.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
+#include "nm-setting-loopback.h"
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-match.h"
@@ -360,6 +361,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME,
.get_setting_gtype = nm_setting_ip_tunnel_get_type,
},
+ [NM_META_SETTING_TYPE_LOOPBACK] =
+ {
+ .meta_type = NM_META_SETTING_TYPE_LOOPBACK,
+ .setting_priority = NM_SETTING_PRIORITY_HW_BASE,
+ .setting_name = NM_SETTING_LOOPBACK_SETTING_NAME,
+ .get_setting_gtype = nm_setting_loopback_get_type,
+ },
[NM_META_SETTING_TYPE_MACSEC] =
{
.meta_type = NM_META_SETTING_TYPE_MACSEC,
@@ -610,6 +618,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_INFINIBAND,
NM_META_SETTING_TYPE_IP_TUNNEL,
+ NM_META_SETTING_TYPE_LOOPBACK,
NM_META_SETTING_TYPE_MACSEC,
NM_META_SETTING_TYPE_MACVLAN,
NM_META_SETTING_TYPE_OVS_BRIDGE,
diff --git a/src/libnm-core-impl/nm-setting-loopback.c b/src/libnm-core-impl/nm-setting-loopback.c
new file mode 100644
index 0000000000..ea777491a8
--- /dev/null
+++ b/src/libnm-core-impl/nm-setting-loopback.c
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#include "libnm-core-impl/nm-default-libnm-core.h"
+
+#include "nm-setting-loopback.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-loopback
+ * @short_description: Describes connection properties for loopback interfaces
+ *
+ * The #NMSettingLoopback object is a #NMSetting subclass that describes properties
+ * necessary for connection to loopback devices
+ **/
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingLoopback, PROP_MTU, );
+
+typedef struct {
+ guint32 mtu;
+} NMSettingLoopbackPrivate;
+
+/**
+ * NMSettingLoopback:
+ *
+ * Loopback Link Settings
+ *
+ * Since: 1.42
+ */
+struct _NMSettingLoopback {
+ NMSetting parent;
+ NMSettingLoopbackPrivate _priv;
+};
+
+struct _NMSettingLoopbackClass {
+ NMSettingClass parent;
+};
+
+#define NM_SETTING_LOOPBACK_GET_PRIVATE(self) \
+ _NM_GET_PRIVATE(self, NMSettingLoopback, NM_IS_SETTING_LOOPBACK)
+
+G_DEFINE_TYPE(NMSettingLoopback, nm_setting_loopback, NM_TYPE_SETTING)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_loopback_get_mtu:
+ * @setting: the #NMSettingLoopback
+ *
+ * Returns: the #NMSettingLoopback:mtu property of the setting
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_loopback_get_mtu(NMSettingLoopback *setting)
+{
+ g_return_val_if_fail(NM_IS_SETTING_LOOPBACK(setting), 0);
+
+ return NM_SETTING_LOOPBACK_GET_PRIVATE(setting)->mtu;
+}
+
+static gboolean
+verify(NMSetting *setting, NMConnection *connection, GError **error)
+{
+ if (connection) {
+ NMSettingIPConfig *s_ip4;
+ NMSettingIPConfig *s_ip6;
+ const char *method;
+
+ if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))) {
+ if ((method = nm_setting_ip_config_get_method(s_ip4))
+ && !NM_IN_STRSET(method,
+ NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+ NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("ipv4 method \"%s\" is not supported for loopback"),
+ method);
+ g_prefix_error(error,
+ "%s.%s: ",
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_METHOD);
+ return FALSE;
+ }
+ if (!NM_IN_SET(nm_setting_ip4_config_get_link_local(NM_SETTING_IP4_CONFIG(s_ip4)),
+ NM_SETTING_IP4_LL_DEFAULT,
+ NM_SETTING_IP4_LL_AUTO,
+ NM_SETTING_IP4_LL_DISABLED)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("ipv4.link-local cannot be enabled for loopback"));
+ g_prefix_error(error,
+ "%s.%s: ",
+ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP4_CONFIG_LINK_LOCAL);
+ return FALSE;
+ }
+ }
+ if ((s_ip6 = nm_connection_get_setting_ip6_config(connection))) {
+ if ((method = nm_setting_ip_config_get_method(s_ip6))
+ && !NM_IN_STRSET(method,
+ NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+ NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+ g_set_error(error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("ipv6 method \"%s\" is not supported for loopback"),
+ method);
+ g_prefix_error(error,
+ "%s.%s: ",
+ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_METHOD);
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_loopback_init(NMSettingLoopback *setting)
+{}
+
+/**
+ * nm_setting_loopback_new:
+ *
+ * Creates a new #NMSettingLoopback object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingLoopback object
+ *
+ * Since: 1.42
+ **/
+NMSetting *
+nm_setting_loopback_new(void)
+{
+ return g_object_new(NM_TYPE_SETTING_LOOPBACK, NULL);
+}
+
+static void
+nm_setting_loopback_class_init(NMSettingLoopbackClass *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;
+
+ /**
+ * NMSettingLoopback:mtu:
+ *
+ * If non-zero, only transmit packets of the specified size or smaller,
+ * breaking larger packets up into multiple Ethernet frames.
+ *
+ * Since: 1.42
+ **/
+ /* ---ifcfg-rh---
+ * property: mtu
+ * variable: MTU
+ * description: MTU of the interface.
+ * ---end---
+ */
+ _nm_setting_property_define_direct_uint32(properties_override,
+ obj_properties,
+ NM_SETTING_LOOPBACK_MTU,
+ PROP_MTU,
+ 0,
+ G_MAXUINT32,
+ 0,
+ NM_SETTING_PARAM_FUZZY_IGNORE,
+ NMSettingLoopback,
+ _priv.mtu);
+
+ g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+ _nm_setting_class_commit(setting_class,
+ NM_META_SETTING_TYPE_LOOPBACK,
+ NULL,
+ properties_override,
+ 0);
+}
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index 50da9cae5e..b8267de487 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -121,7 +121,7 @@ test_nm_meta_setting_types_by_priority(void)
G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM
== G_N_ELEMENTS(nm_meta_setting_types_by_priority));
- G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 52);
+ G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 53);
arr = g_ptr_array_new_with_free_func(g_object_unref);
diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h
index df9a6d047b..ba28bc75f1 100644
--- a/src/libnm-core-intern/nm-core-internal.h
+++ b/src/libnm-core-intern/nm-core-internal.h
@@ -43,6 +43,7 @@
#include "nm-setting-ip-tunnel.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
+#include "nm-setting-loopback.h"
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-match.h"
diff --git a/src/libnm-core-intern/nm-meta-setting-base-impl.h b/src/libnm-core-intern/nm-meta-setting-base-impl.h
index 1268865954..ea2175f84c 100644
--- a/src/libnm-core-intern/nm-meta-setting-base-impl.h
+++ b/src/libnm-core-intern/nm-meta-setting-base-impl.h
@@ -127,6 +127,7 @@ typedef enum _nm_packed {
NM_META_SETTING_TYPE_IP_TUNNEL,
NM_META_SETTING_TYPE_IP4_CONFIG,
NM_META_SETTING_TYPE_IP6_CONFIG,
+ NM_META_SETTING_TYPE_LOOPBACK,
NM_META_SETTING_TYPE_MACSEC,
NM_META_SETTING_TYPE_MACVLAN,
NM_META_SETTING_TYPE_MATCH,
diff --git a/src/libnm-core-public/meson.build b/src/libnm-core-public/meson.build
index f1da4d023d..beeeaedd47 100644
--- a/src/libnm-core-public/meson.build
+++ b/src/libnm-core-public/meson.build
@@ -29,6 +29,7 @@ libnm_core_headers = files(
'nm-setting-ip-tunnel.h',
'nm-setting-ip4-config.h',
'nm-setting-ip6-config.h',
+ 'nm-setting-loopback.h',
'nm-setting-macsec.h',
'nm-setting-macvlan.h',
'nm-setting-match.h',
diff --git a/src/libnm-core-public/nm-core-types.h b/src/libnm-core-public/nm-core-types.h
index c86c28fa47..4db0c86e3d 100644
--- a/src/libnm-core-public/nm-core-types.h
+++ b/src/libnm-core-public/nm-core-types.h
@@ -35,6 +35,7 @@ typedef struct _NMSettingIP6Config NMSettingIP6Config;
typedef struct _NMSettingIPConfig NMSettingIPConfig;
typedef struct _NMSettingIPTunnel NMSettingIPTunnel;
typedef struct _NMSettingInfiniband NMSettingInfiniband;
+typedef struct _NMSettingLoopback NMSettingLoopback;
typedef struct _NMSettingMacsec NMSettingMacsec;
typedef struct _NMSettingMacvlan NMSettingMacvlan;
typedef struct _NMSettingMatch NMSettingMatch;
diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h
index 6e1a84a1dd..0baf14bbc3 100644
--- a/src/libnm-core-public/nm-dbus-interface.h
+++ b/src/libnm-core-public/nm-dbus-interface.h
@@ -39,6 +39,7 @@
#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
+#define NM_DBUS_INTERFACE_DEVICE_LOOPBACK NM_DBUS_INTERFACE_DEVICE ".Loopback"
#define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec"
#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan"
#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem"
@@ -218,6 +219,7 @@ typedef enum {
* @NM_DEVICE_TYPE_WIREGUARD: a WireGuard interface
* @NM_DEVICE_TYPE_WIFI_P2P: an 802.11 Wi-Fi P2P device. Since: 1.16.
* @NM_DEVICE_TYPE_VRF: A VRF (Virtual Routing and Forwarding) interface. Since: 1.24.
+ * @NM_DEVICE_TYPE_LOOPBACK: a loopback interface. Since: 1.42.
*
* #NMDeviceType values indicate the type of hardware represented by a
* device object.
@@ -255,6 +257,7 @@ typedef enum {
NM_DEVICE_TYPE_WIREGUARD = 29,
NM_DEVICE_TYPE_WIFI_P2P = 30,
NM_DEVICE_TYPE_VRF = 31,
+ NM_DEVICE_TYPE_LOOPBACK = 32,
} NMDeviceType;
/**
diff --git a/src/libnm-core-public/nm-setting-loopback.h b/src/libnm-core-public/nm-setting-loopback.h
new file mode 100644
index 0000000000..96b9c6d890
--- /dev/null
+++ b/src/libnm-core-public/nm-setting-loopback.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_LOOPBACK_H__
+#define __NM_SETTING_LOOPBACK_H__
+
+#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_LOOPBACK (nm_setting_loopback_get_type())
+#define NM_SETTING_LOOPBACK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_LOOPBACK, NMSettingLoopback))
+#define NM_SETTING_LOOPBACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_SETTING_LOOPBACKCONFIG, NMSettingLoopbackClass))
+#define NM_IS_SETTING_LOOPBACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_LOOPBACK))
+#define NM_IS_SETTING_LOOPBACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_LOOPBACK))
+#define NM_SETTING_LOOPBACK_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_SETTING_LOOPBACK, NMSettingLoopbackClass))
+
+#define NM_SETTING_LOOPBACK_SETTING_NAME "loopback"
+
+#define NM_SETTING_LOOPBACK_MTU "mtu"
+
+typedef struct _NMSettingLoopbackClass NMSettingLoopbackClass;
+
+NM_AVAILABLE_IN_1_42
+GType nm_setting_loopback_get_type(void);
+NM_AVAILABLE_IN_1_42
+NMSetting *nm_setting_loopback_new(void);
+
+NM_AVAILABLE_IN_1_42
+guint32 nm_setting_loopback_get_mtu(NMSettingLoopback *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_LOOPBACK_H__ */
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index 885829e059..5b970686d7 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -4564,7 +4564,26 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self,
c_list_for_each (iter, &head_entry->lst_entries_head) {
const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj;
- if (!IS_IPv4) {
+ if (IS_IPv4) {
+ const NMPlatformIP4Address *a4 = NMP_OBJECT_CAST_IP4_ADDRESS(obj);
+
+ if (a4->address == NM_IPV4LO_ADDR1 && a4->plen == NM_IPV4LO_PREFIXLEN) {
+ const NMPlatformIP4Address addr = (NMPlatformIP4Address){
+ .ifindex = NM_LOOPBACK_IFINDEX,
+ .address = NM_IPV4LO_ADDR1,
+ .peer_address = NM_IPV4LO_ADDR1,
+ .plen = NM_IPV4LO_PREFIXLEN,
+ .use_ip4_broadcast_address = TRUE,
+ };
+
+ if (nm_platform_ip4_address_cmp(a4,
+ &addr,
+ NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY)
+ == 0) {
+ continue;
+ }
+ }
+ } else {
const NMPlatformIP6Address *a6 = NMP_OBJECT_CAST_IP6_ADDRESS(obj);
if (NM_FLAGS_HAS(a6->n_ifa_flags, IFA_F_SECONDARY)
@@ -4665,6 +4684,44 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self,
* pruning them. */
if (NM_IS_IPv4(addr_family)) {
+ if (ifindex == NM_LOOPBACK_IFINDEX
+ && NM_IN_SET(rt->r4.network, NM_IPV4LO_ADDR1, NM_IPV4LO_NETWORK)) {
+ NMPlatformIP4Route r;
+
+ if (rt->r4.network == NM_IPV4LO_ADDR1) {
+ r = (NMPlatformIP4Route){
+ .ifindex = NM_LOOPBACK_IFINDEX,
+ .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL),
+ .table_coerced = nm_platform_route_table_coerce(local_table),
+ .network = NM_IPV4LO_ADDR1,
+ .plen = 32,
+ .metric = 0,
+ .rt_source = NM_IPV4LO_ADDR1,
+ .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST),
+ .pref_src = NM_IPV4LO_ADDR1,
+ };
+ } else {
+ r = (NMPlatformIP4Route){
+ .ifindex = NM_LOOPBACK_IFINDEX,
+ .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL),
+ .table_coerced = nm_platform_route_table_coerce(local_table),
+ .network = NM_IPV4LO_NETWORK,
+ .plen = NM_IPV4LO_PREFIXLEN,
+ .metric = 0,
+ .rt_source = NM_IPV4LO_ADDR1,
+ .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST),
+ .pref_src = NM_IPV4LO_ADDR1,
+ };
+ }
+
+ if (nm_platform_ip4_route_cmp(&rt->r4,
+ &r,
+ NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
+ == 0) {
+ continue;
+ }
+ }
+
/* for each IPv4 address kernel adds a route like
*
* local $ADDR dev $IFACE table local proto kernel scope host src $PRIMARY_ADDR
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index 32773b67ad..36df3db94f 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -2464,4 +2464,29 @@ void nm_platform_ip6_dadfailed_set(NMPlatform *self,
const struct in6_addr *ip6,
gboolean failed);
+/*****************************************************************************/
+
+static inline NMPlatformIP4Address *
+nm_platform_ip4_address_init_loopback_addr1(NMPlatformIP4Address *a)
+{
+ *a = ((NMPlatformIP4Address){
+ .address = NM_IPV4LO_ADDR1,
+ .peer_address = NM_IPV4LO_ADDR1,
+ .ifindex = NM_LOOPBACK_IFINDEX,
+ .plen = NM_IPV4LO_PREFIXLEN,
+ });
+ return a;
+}
+
+static inline NMPlatformIP6Address *
+nm_platform_ip6_address_init_loopback(NMPlatformIP6Address *a)
+{
+ *a = ((NMPlatformIP6Address){
+ .address = IN6ADDR_LOOPBACK_INIT,
+ .ifindex = NM_LOOPBACK_IFINDEX,
+ .plen = 128,
+ });
+ return a;
+}
+
#endif /* __NETWORKMANAGER_PLATFORM_H__ */
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.c b/src/libnmc-setting/nm-meta-setting-base-impl.c
index 5e627cfc7d..e93a1555dc 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.c
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.c
@@ -34,6 +34,7 @@
#include "nm-setting-ip-tunnel.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
+#include "nm-setting-loopback.h"
#include "nm-setting-macsec.h"
#include "nm-setting-macvlan.h"
#include "nm-setting-match.h"
@@ -360,6 +361,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
.setting_name = NM_SETTING_IP_TUNNEL_SETTING_NAME,
.get_setting_gtype = nm_setting_ip_tunnel_get_type,
},
+ [NM_META_SETTING_TYPE_LOOPBACK] =
+ {
+ .meta_type = NM_META_SETTING_TYPE_LOOPBACK,
+ .setting_priority = NM_SETTING_PRIORITY_HW_BASE,
+ .setting_name = NM_SETTING_LOOPBACK_SETTING_NAME,
+ .get_setting_gtype = nm_setting_loopback_get_type,
+ },
[NM_META_SETTING_TYPE_MACSEC] =
{
.meta_type = NM_META_SETTING_TYPE_MACSEC,
@@ -610,6 +618,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
NM_META_SETTING_TYPE_GSM,
NM_META_SETTING_TYPE_INFINIBAND,
NM_META_SETTING_TYPE_IP_TUNNEL,
+ NM_META_SETTING_TYPE_LOOPBACK,
NM_META_SETTING_TYPE_MACSEC,
NM_META_SETTING_TYPE_MACVLAN,
NM_META_SETTING_TYPE_OVS_BRIDGE,
diff --git a/src/libnmc-setting/nm-meta-setting-base-impl.h b/src/libnmc-setting/nm-meta-setting-base-impl.h
index 1268865954..ea2175f84c 100644
--- a/src/libnmc-setting/nm-meta-setting-base-impl.h
+++ b/src/libnmc-setting/nm-meta-setting-base-impl.h
@@ -127,6 +127,7 @@ typedef enum _nm_packed {
NM_META_SETTING_TYPE_IP_TUNNEL,
NM_META_SETTING_TYPE_IP4_CONFIG,
NM_META_SETTING_TYPE_IP6_CONFIG,
+ NM_META_SETTING_TYPE_LOOPBACK,
NM_META_SETTING_TYPE_MACSEC,
NM_META_SETTING_TYPE_MACVLAN,
NM_META_SETTING_TYPE_MATCH,
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index c4e5e7de03..40dd28f31b 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -6561,6 +6561,21 @@ static const NMMetaPropertyInfo *const property_infos_IP_TUNNEL[] = {
NULL
};
+#undef _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_LOOPBACK
+static const NMMetaPropertyInfo *const property_infos_LOOPBACK[] = {
+ PROPERTY_INFO_WITH_DESC (NM_SETTING_LOOPBACK_MTU,
+ .is_cli_option = TRUE,
+ .property_alias = "mtu",
+ .prompt = N_("MTU"),
+ .property_type = &_pt_gobject_mtu,
+ .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (mtu,
+ .get_fcn = MTU_GET_FCN (NMSettingLoopback, nm_setting_loopback_get_mtu),
+ ),
+ ),
+ NULL
+};
+
#undef _CURRENT_NM_META_SETTING_TYPE
#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_MACSEC
static const NMMetaPropertyInfo *const property_infos_MACSEC[] = {
@@ -8317,6 +8332,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
#define SETTING_PRETTY_NAME_IP4_CONFIG N_("IPv4 protocol")
#define SETTING_PRETTY_NAME_IP6_CONFIG N_("IPv6 protocol")
#define SETTING_PRETTY_NAME_IP_TUNNEL N_("IP-tunnel settings")
+#define SETTING_PRETTY_NAME_LOOPBACK N_("Loopback settings")
#define SETTING_PRETTY_NAME_MACSEC N_("MACsec connection")
#define SETTING_PRETTY_NAME_MACVLAN N_("macvlan connection")
#define SETTING_PRETTY_NAME_MATCH N_("Match")
@@ -8476,6 +8492,12 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE),
),
),
+ SETTING_INFO (LOOPBACK,
+ .valid_parts = NM_META_SETTING_VALID_PARTS (
+ NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
+ NM_META_SETTING_VALID_PART_ITEM (LOOPBACK, TRUE),
+ ),
+ ),
SETTING_INFO (MACSEC,
.valid_parts = NM_META_SETTING_VALID_PARTS (
NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE),
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index 2873abd127..2f2d561297 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -432,5 +432,6 @@
#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 NM_TERNARY_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 NM_TERNARY_TRUE (1).")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to NM_TERNARY_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 NM_TERNARY_FALSE (0), the hostname can be set from this device even if it doesn't have the default route. When set to NM_TERNARY_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 NM_TERNARY_FALSE (0).")
#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_PRIORITY N_("The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority). A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname.")
+#define DESCRIBE_DOC_NM_SETTING_LOOPBACK_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with exernal-ids for OVS.")
#define DESCRIBE_DOC_NM_SETTING_VETH_PEER N_("This property specifies the peer interface name of the veth. This property is mandatory.")
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 941b1e353e..41be4a8e8c 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -762,6 +762,11 @@
+
+
+