summary refs log tree commit diff
path: root/libnm-core
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-02-19 23:31:24 +0100
committerMichael Biebl <biebl@debian.org>2020-02-19 23:31:24 +0100
commite536d40eaea5dcdc0743b0a5e8e17faa46608a50 (patch)
tree0749832d8655215dac973866b4e724f3aea35448 /libnm-core
parentf3c6d0765dff885e168b94f28e06ecc640315a74 (diff)
New upstream version 1.22.8 upstream/1.22.8
Diffstat (limited to 'libnm-core')
-rw-r--r--libnm-core/meson.build5
-rw-r--r--libnm-core/nm-connection-private.h1
-rw-r--r--libnm-core/nm-connection.c1
-rw-r--r--libnm-core/nm-setting-bond.c4
-rw-r--r--libnm-core/nm-setting-connection.c71
-rw-r--r--libnm-core/nm-setting-infiniband.c4
-rw-r--r--libnm-core/nm-setting-ip-config.c15
-rw-r--r--libnm-core/nm-setting-ip-tunnel.c2
-rw-r--r--libnm-core/nm-setting-ip6-config.c63
-rw-r--r--libnm-core/nm-setting-ip6-config.h4
-rw-r--r--libnm-core/nm-setting-macvlan.c2
-rw-r--r--libnm-core/nm-setting-ovs-interface.c51
-rw-r--r--libnm-core/nm-setting-pppoe.c2
-rw-r--r--libnm-core/nm-setting-vlan.c2
-rw-r--r--libnm-core/nm-setting-vxlan.c2
-rw-r--r--libnm-core/nm-utils.c43
-rw-r--r--libnm-core/nm-version.h6
-rw-r--r--libnm-core/tests/test-general.c194
18 files changed, 382 insertions, 90 deletions
diff --git a/libnm-core/meson.build b/libnm-core/meson.build
index 4c72590e..469d4161 100644
--- a/libnm-core/meson.build
+++ b/libnm-core/meson.build
@@ -188,9 +188,12 @@ links = [
   libnm_libnm_core_intern,
 ]
 
+libnm_core_c_args = common_c_flags
+
 if enable_json_validation
   libnm_core_sources += files('nm-json.c')
   deps += jansson_dep
+  libnm_core_c_args += ['-fcommon']
 endif
 
 libnm_core = static_library(
@@ -198,7 +201,7 @@ libnm_core = static_library(
   sources: libnm_core_sources + libnm_core_enum_sources + nm_meta_setting_source + [nm_version_macro_header],
   include_directories: top_inc,
   dependencies: deps,
-  c_args: common_c_flags,
+  c_args: libnm_core_c_args,
   link_with: links,
 )
 
diff --git a/libnm-core/nm-connection-private.h b/libnm-core/nm-connection-private.h
index ea7583e0..034c350f 100644
--- a/libnm-core/nm-connection-private.h
+++ b/libnm-core/nm-connection-private.h
@@ -27,6 +27,7 @@ int _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self
                                                      NMConnection *connection,
                                                      gboolean normalize,
                                                      gboolean *out_modified,
+                                                     const char **normalized_type,
                                                      GError **error);
 
 #endif  /* __NM_CONNECTION_PRIVATE_H__ */
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 307b9d62..4146fd55 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -1235,6 +1235,7 @@ _normalize_ovs_interface_type (NMConnection *self)
 	                                                     self,
 	                                                     TRUE,
 	                                                     &modified,
+	                                                     NULL,
 	                                                     NULL);
 	if (v != TRUE)
 		g_return_val_if_reached (modified);
diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c
index 52da8ccf..f4c35663 100644
--- a/libnm-core/nm-setting-bond.c
+++ b/libnm-core/nm-setting-bond.c
@@ -225,7 +225,7 @@ validate_ifname (const char *name, const char *value)
 	if (!value || !value[0])
 		return FALSE;
 
-	return nm_utils_is_valid_iface_name (value, NULL);
+	return nm_utils_ifname_valid_kernel (value, NULL);
 }
 
 /**
@@ -611,7 +611,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	if (strcmp (mode_new, "active-backup") == 0) {
 		GError *tmp_error = NULL;
 
-		if (primary && !nm_utils_is_valid_iface_name (primary, &tmp_error)) {
+		if (primary && !nm_utils_ifname_valid_kernel (primary, &tmp_error)) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index a13e3f23..17058ea7 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -973,20 +973,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (priv->interface_name) {
-		GError *tmp_error = NULL;
-
-		if (!nm_utils_is_valid_iface_name (priv->interface_name, &tmp_error)) {
-			g_set_error (error,
-			             NM_CONNECTION_ERROR,
-			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			             "'%s': %s", priv->interface_name, tmp_error->message);
-			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
-			g_error_free (tmp_error);
-			return FALSE;
-		}
-	}
-
 	type = priv->type;
 	if (!type) {
 		if (   !connection
@@ -1044,6 +1030,34 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
+	if (priv->interface_name) {
+		GError *tmp_error = NULL;
+		gboolean valid_ifname = FALSE;
+
+		/* do not perform a interface name length check for OVS connection types
+		 * as they don't have a corresponding kernel link that enforces the 15 bytes limit.
+		 * Here we're whitelisting the OVS interface type as well, even if most OVS
+		 * iface types do have the limit, to let the OVS specific nm-setting verify whether the iface name
+		 * is good or not according to the internal type (internal, patch, ...) */
+		if (NM_IN_STRSET (type,
+		                  NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+		                  NM_SETTING_OVS_PORT_SETTING_NAME,
+		                  NM_SETTING_OVS_INTERFACE_SETTING_NAME))
+			valid_ifname = nm_utils_ifname_valid (priv->interface_name, NMU_IFACE_OVS, &tmp_error);
+		else
+			valid_ifname = nm_utils_ifname_valid (priv->interface_name, NMU_IFACE_KERNEL, &tmp_error);
+
+		if (!valid_ifname) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             "'%s': %s", priv->interface_name, tmp_error->message);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+			g_error_free (tmp_error);
+			return FALSE;
+		}
+	}
+
 	is_slave = FALSE;
 	slave_setting_type = NULL;
 	slave_type = priv->slave_type;
@@ -1276,7 +1290,7 @@ nm_setting_connection_set_interface_name (NMSetting *setting,
 	 * overridden by a valid connection.interface-name.
 	 */
 	interface_name = find_virtual_interface_name (connection_dict);
-	if (!interface_name || nm_utils_is_valid_iface_name (interface_name, NULL))
+	if (!interface_name || nm_utils_ifname_valid_kernel (interface_name, NULL))
 		interface_name = g_variant_get_string (value, NULL);
 
 	g_object_set (G_OBJECT (setting),
@@ -2173,13 +2187,16 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *
 	 * Whether mDNS is enabled for the connection.
 	 *
-	 * The permitted values are: yes: register hostname and resolving
-	 * for the connection, no: disable mDNS for the interface, resolve:
-	 * do not register hostname but allow resolving of mDNS host names.
-	 *
-	 * This feature requires a plugin which supports mDNS. One such
-	 * plugin is dns-systemd-resolved.
+	 * The permitted values are: "yes" (2) register hostname and resolving
+	 * for the connection, "no" (0) disable mDNS for the interface, "resolve"
+	 * (1) do not register hostname but allow resolving of mDNS host names
+	 * and "default" (-1) to allow lookup of a global default in NetworkManager.conf.
+	 * If unspecified, "default" ultimately depends on the DNS plugin (which
+	 * for systemd-resolved currently means "no").
 	 *
+	 * This feature requires a plugin which supports mDNS. Otherwise the
+	 * setting has no effect. One such plugin is dns-systemd-resolved.
+*
 	 * Since: 1.12
 	 **/
 	/* ---ifcfg-rh---
@@ -2206,12 +2223,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * System (DNS) packet format that allows both IPv4 and IPv6 hosts
 	 * to perform name resolution for hosts on the same local link.
 	 *
-	 * The permitted values are: yes: register hostname and resolving
-	 * for the connection, no: disable LLMNR for the interface, resolve:
-	 * do not register hostname but allow resolving of LLMNR host names.
+	 * The permitted values are: "yes" (2) register hostname and resolving
+	 * for the connection, "no" (0) disable LLMNR for the interface, "resolve"
+	 * (1) do not register hostname but allow resolving of LLMNR host names
+	 * If unspecified, "default" ultimately depends on the DNS plugin (which
+	 * for systemd-resolved currently means "yes").
 	 *
-	 * This feature requires a plugin which supports LLMNR. One such
-	 * plugin is dns-systemd-resolved.
+	 * This feature requires a plugin which supports LLMNR. Otherwise the
+	 * setting has no effect. One such plugin is dns-systemd-resolved.
 	 *
 	 * Since: 1.14
 	 **/
diff --git a/libnm-core/nm-setting-infiniband.c b/libnm-core/nm-setting-infiniband.c
index d03a7a2a..fcaa1f9a 100644
--- a/libnm-core/nm-setting-infiniband.c
+++ b/libnm-core/nm-setting-infiniband.c
@@ -177,7 +177,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	if (priv->parent) {
 		GError *tmp_error = NULL;
 
-		if (!nm_utils_is_valid_iface_name (priv->parent, &tmp_error)) {
+		if (!nm_utils_ifname_valid_kernel (priv->parent, &tmp_error)) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -214,7 +214,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 
 		if (!interface_name)
 			;
-		else if (!nm_utils_is_valid_iface_name (interface_name, &tmp_error)) {
+		else if (!nm_utils_ifname_valid_kernel (interface_name, &tmp_error)) {
 			/* report the error for NMSettingConnection:interface-name, because
 			 * it's that property that is invalid -- although we currently verify()
 			 * NMSettingInfiniband.
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 49a3efbd..7f1690ac 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -4921,7 +4921,7 @@ verify_label (const char *label)
 	if (!p)
 		return FALSE;
 	iface = g_strndup (label, p - label);
-	if (!nm_utils_is_valid_iface_name (iface, NULL)) {
+	if (!nm_utils_ifname_valid_kernel (iface, NULL)) {
 		g_free (iface);
 		return FALSE;
 	}
@@ -5637,10 +5637,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * When using dns=default, servers with higher priority will be on top of
 	 * resolv.conf.  To prioritize a given server over another one within the
 	 * same connection, just specify them in the desired order.  When multiple
-	 * devices have configurations with the same priority, the one with an
-	 * active default route will be preferred.  Negative values have the special
+	 * devices have configurations with the same priority, VPNs will be
+	 * considered first, then devices with the best (lowest metric) default
+	 * route and then all other devices.  Negative values have the special
 	 * effect of excluding other configurations with a greater priority value;
-	 * so in presence of at least a negative priority, only DNS servers from
+	 * so in presence of at least one negative priority, only DNS servers from
 	 * connections with the lowest priority value will be used.
 	 *
 	 * When using a DNS resolver that supports Conditional Forwarding as dns=dnsmasq or
@@ -5871,7 +5872,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	/**
 	 * NMSettingIPConfig:dhcp-timeout:
 	 *
-	 * A timeout for a DHCP transaction in seconds.
+	 * A timeout for a DHCP transaction in seconds. If zero (the default), a
+	 * globally configured default is used. If still unspecified, a device specific
+	 * timeout is used (usually 45 seconds).
+	 *
+	 * Set to 2147483647 (MAXINT32) for infinity.
 	 **/
 	obj_properties[PROP_DHCP_TIMEOUT] =
 	    g_param_spec_int (NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, "", "",
diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c
index 3df89cdd..ae1df194 100644
--- a/libnm-core/nm-setting-ip-tunnel.c
+++ b/libnm-core/nm-setting-ip-tunnel.c
@@ -329,7 +329,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	}
 
 	if (   priv->parent
-	    && !nm_utils_is_valid_iface_name (priv->parent, NULL)
+	    && !nm_utils_ifname_valid_kernel (priv->parent, NULL)
 	    && !nm_utils_is_uuid (priv->parent)) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index 4cbda830..f3306ade 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -43,13 +43,15 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_ADDR_GEN_MODE,
 	PROP_TOKEN,
 	PROP_DHCP_DUID,
+	PROP_RA_TIMEOUT,
 );
 
 typedef struct {
-	NMSettingIP6ConfigPrivacy ip6_privacy;
-	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
 	char *token;
 	char *dhcp_duid;
+	NMSettingIP6ConfigPrivacy ip6_privacy;
+	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+	gint32 ra_timeout;
 } NMSettingIP6ConfigPrivate;
 
 G_DEFINE_TYPE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING_IP_CONFIG)
@@ -134,6 +136,23 @@ nm_setting_ip6_config_get_dhcp_duid (NMSettingIP6Config *setting)
 	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dhcp_duid;
 }
 
+/**
+ * nm_setting_ip6_config_get_ra_timeout:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns: The configured %NM_SETTING_IP6_CONFIG_RA_TIMEOUT value with the
+ * timeout for router advertisements in seconds.
+ *
+ * Since: 1.24, 1.22.8
+ **/
+gint32
+nm_setting_ip6_config_get_ra_timeout (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ra_timeout;
+}
+
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -475,6 +494,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_DHCP_DUID:
 		g_value_set_string (value, priv->dhcp_duid);
 		break;
+	case PROP_RA_TIMEOUT:
+		g_value_set_int (value, priv->ra_timeout);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -502,6 +524,9 @@ set_property (GObject *object, guint prop_id,
 		g_free (priv->dhcp_duid);
 		priv->dhcp_duid = g_value_dup_string (value);
 		break;
+	case PROP_RA_TIMEOUT:
+		priv->ra_timeout = g_value_get_int (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -658,6 +683,14 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	 */
 
 	/* ---ifcfg-rh---
+	 * property: dhcp-timeout
+	 * variable: IPV6_DHCP_TIMEOUT(+)
+	 * description: A timeout after which the DHCP transaction fails in case of no response.
+	 * example: IPV6_DHCP_TIMEOUT=10
+	 * ---end---
+	 */
+
+	/* ---ifcfg-rh---
 	 * property: dhcp-hostname-flags
 	 * variable: DHCPV6_HOSTNAME_FLAGS
 	 * description: flags for the DHCP hostname property
@@ -829,6 +862,32 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	                         G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * NMSettingIP6Config:ra-timeout:
+	 *
+	 * A timeout for waiting Router Advertisements in seconds. If zero (the default), a
+	 * globally configured default is used. If still unspecified, the timeout depends on the
+	 * sysctl settings of the device.
+	 *
+	 * Set to 2147483647 (MAXINT32) for infinity.
+	 *
+	 * Since: 1.24, 1.22.8
+	 **/
+	/* ---ifcfg-rh---
+	 * property: dhcp-timeout
+	 * variable: IPV6_RA_TIMEOUT(+)
+	 * description: A timeout for waiting Router Advertisements in seconds.
+	 * example: IPV6_RA_TIMEOUT=10
+	 * ---end---
+	 */
+
+	obj_properties[PROP_RA_TIMEOUT] =
+	    g_param_spec_int (NM_SETTING_IP6_CONFIG_RA_TIMEOUT, "", "",
+	                      0, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      NM_SETTING_PARAM_FUZZY_IGNORE |
+	                      G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * NMSettingIP6Config:dhcp-duid:
 	 *
 	 * A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp
diff --git a/libnm-core/nm-setting-ip6-config.h b/libnm-core/nm-setting-ip6-config.h
index 86a39a8e..4173c3b0 100644
--- a/libnm-core/nm-setting-ip6-config.h
+++ b/libnm-core/nm-setting-ip6-config.h
@@ -31,6 +31,8 @@ G_BEGIN_DECLS
 
 #define NM_SETTING_IP6_CONFIG_DHCP_DUID "dhcp-duid"
 
+#define NM_SETTING_IP6_CONFIG_RA_TIMEOUT "ra-timeout"
+
 /**
  * NM_SETTING_IP6_CONFIG_METHOD_IGNORE:
  *
@@ -159,6 +161,8 @@ NM_AVAILABLE_IN_1_4
 const char *nm_setting_ip6_config_get_token (NMSettingIP6Config *setting);
 NM_AVAILABLE_IN_1_12
 const char *nm_setting_ip6_config_get_dhcp_duid (NMSettingIP6Config *setting);
+NM_AVAILABLE_IN_1_22_8
+gint32 nm_setting_ip6_config_get_ra_timeout (NMSettingIP6Config *setting);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting-macvlan.c b/libnm-core/nm-setting-macvlan.c
index cb7778f1..d62184c2 100644
--- a/libnm-core/nm-setting-macvlan.c
+++ b/libnm-core/nm-setting-macvlan.c
@@ -120,7 +120,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 
 	if (priv->parent) {
 		if (   !nm_utils_is_uuid (priv->parent)
-		    && !nm_utils_is_valid_iface_name (priv->parent, NULL)) {
+		    && !nm_utils_ifname_valid_kernel (priv->parent, NULL)) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/libnm-core/nm-setting-ovs-interface.c b/libnm-core/nm-setting-ovs-interface.c
index ecefd6a5..849b9a4e 100644
--- a/libnm-core/nm-setting-ovs-interface.c
+++ b/libnm-core/nm-setting-ovs-interface.c
@@ -67,6 +67,7 @@ _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
                                                  NMConnection *connection,
                                                  gboolean normalize,
                                                  gboolean *out_modified,
+                                                 const char **normalized_type,
                                                  GError **error)
 {
 	const char *type;
@@ -82,9 +83,11 @@ _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
 	} else
 		g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE);
 
+	g_return_val_if_fail (!normalized_type || !(*normalized_type), FALSE);
+
 	NM_SET_OUT (out_modified, FALSE);
 
-	type = self ? self->type : NULL;
+	type = self->type;
 
 	if (   type
 	    && !NM_IN_STRSET (type, "internal", "system", "patch", "dpdk")) {
@@ -212,6 +215,10 @@ _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self,
 		type = "internal";
 	else
 		type = "system";
+
+	if (normalized_type)
+		*normalized_type = type;
+
 normalize:
 	if (!normalize) {
 		if (!self) {
@@ -246,9 +253,11 @@ static int
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (setting);
+	NMSettingConnection *s_con = NULL;
+	const char *normalized_type = NULL;
+	int result = NM_SETTING_VERIFY_ERROR;
 
 	if (connection) {
-		NMSettingConnection *s_con;
 		const char *slave_type;
 
 		s_con = nm_connection_get_setting_connection (connection);
@@ -286,11 +295,39 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		}
 	}
 
-	return _nm_setting_ovs_interface_verify_interface_type (self,
-	                                                        connection,
-	                                                        FALSE,
-	                                                        NULL,
-	                                                        error);
+	result = _nm_setting_ovs_interface_verify_interface_type (self,
+	                                                          connection,
+	                                                          FALSE,
+	                                                          NULL,
+	                                                          &normalized_type,
+	                                                          error);
+
+	/* From 'man ovs-vswitchd.conf.db': OVS patch interfaces do not have
+	 * a limit on interface name length, all the other types do */
+	if (result != NM_SETTING_VERIFY_ERROR && s_con) {
+		gs_free_error GError *ifname_error = NULL;
+		const char *ifname = nm_setting_connection_get_interface_name (s_con);
+
+		normalized_type = self->type ? self->type : normalized_type;
+
+		if (   ifname
+		    && !nm_streq0 (normalized_type, "patch")
+		    && !nm_utils_ifname_valid (ifname,
+		                               NMU_IFACE_KERNEL,
+		                               &ifname_error)) {
+			g_clear_error (error);
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             "'%s': %s", ifname, ifname_error->message);
+			g_prefix_error (error, "%s.%s: ",
+			                NM_SETTING_CONNECTION_SETTING_NAME,
+			                NM_SETTING_CONNECTION_INTERFACE_NAME);
+			return NM_SETTING_VERIFY_ERROR;
+		}
+	}
+
+	return result;
 }
 
 /*****************************************************************************/
diff --git a/libnm-core/nm-setting-pppoe.c b/libnm-core/nm-setting-pppoe.c
index d9ff2a80..0fee96a1 100644
--- a/libnm-core/nm-setting-pppoe.c
+++ b/libnm-core/nm-setting-pppoe.c
@@ -149,7 +149,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	}
 
 	if (   priv->parent
-	    && !nm_utils_is_valid_iface_name (priv->parent, &local_error)) {
+	    && !nm_utils_ifname_valid_kernel (priv->parent, &local_error)) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c
index 84b974dc..30f43760 100644
--- a/libnm-core/nm-setting-vlan.c
+++ b/libnm-core/nm-setting-vlan.c
@@ -603,7 +603,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 					return FALSE;
 				}
 			}
-		} else if (!nm_utils_is_valid_iface_name (priv->parent, NULL)) {
+		} else if (!nm_utils_ifname_valid_kernel (priv->parent, NULL)) {
 			/* parent must be either a UUID or an interface name */
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
diff --git a/libnm-core/nm-setting-vxlan.c b/libnm-core/nm-setting-vxlan.c
index d363ff20..4e449bc8 100644
--- a/libnm-core/nm-setting-vxlan.c
+++ b/libnm-core/nm-setting-vxlan.c
@@ -358,7 +358,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	}
 
 	if (   priv->parent
-	    && !nm_utils_is_valid_iface_name (priv->parent, NULL)
+	    && !nm_utils_ifname_valid_kernel (priv->parent, NULL)
 	    && !nm_utils_is_uuid (priv->parent)) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 4659734b..90ae4070 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -14,7 +14,6 @@
 #include <libintl.h>
 #include <gmodule.h>
 #include <sys/stat.h>
-#include <net/if.h>
 #include <linux/pkt_sched.h>
 
 #if WITH_JSON_VALIDATION
@@ -4697,7 +4696,7 @@ nm_utils_is_valid_iface_name_utf8safe (const char *utf8safe_name)
 		}
 	}
 
-	return nm_utils_is_valid_iface_name (bin, NULL);
+	return nm_utils_ifname_valid_kernel (bin, NULL);
 }
 
 /**
@@ -4718,45 +4717,9 @@ nm_utils_is_valid_iface_name_utf8safe (const char *utf8safe_name)
 gboolean
 nm_utils_is_valid_iface_name (const char *name, GError **error)
 {
-	int i;
-
-	if (!name) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     _("interface name is missing"));
-		return FALSE;
-	}
-
-	if (name[0] == '\0') {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     _("interface name is too short"));
-		return FALSE;
-	}
-
-	if (   name[0] == '.'
-	    && (   name[1] == '\0'
-	        || (   name[1] == '.'
-	            && name[2] == '\0'))) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     _("interface name is reserved"));
-		return FALSE;
-	}
-
-	for (i = 0; i < IFNAMSIZ; i++) {
-		char ch = name[i];
-
-		if (ch == '\0')
-			return TRUE;
-		if (   NM_IN_SET (ch, '/', ':')
-		    || g_ascii_isspace (ch)) {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     _("interface name contains an invalid character"));
-			return FALSE;
-		}
-	}
+	g_return_val_if_fail (!error || !*error, FALSE);
 
-	g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-	                     _("interface name is longer than 15 characters"));
-	return FALSE;
+	return nm_utils_ifname_valid_kernel (name, error);
 }
 
 /**
diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h
index 8cb0ac50..3fd2f346 100644
--- a/libnm-core/nm-version.h
+++ b/libnm-core/nm-version.h
@@ -229,6 +229,12 @@
 # define NM_AVAILABLE_IN_1_22_2
 #endif
 
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_22_8
+# define NM_AVAILABLE_IN_1_22_8            G_UNAVAILABLE(1,22.8)
+#else
+# define NM_AVAILABLE_IN_1_22_8
+#endif
+
 /*
  * Synchronous API for calling D-Bus in libnm is deprecated. See
  * https://developer.gnome.org/libnm/stable/usage.html#sync-api
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index c1b7b84a..3b75e0f7 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -8319,6 +8319,193 @@ test_integrate_maincontext (gconstpointer test_data)
 	}
 }
 
+static void
+test_connection_ovs_ifname (gconstpointer test_data)
+{
+	const guint TEST_CASE = GPOINTER_TO_UINT (test_data);
+	gs_unref_object NMConnection *con = NULL;
+	NMSettingConnection *s_con = NULL;
+	NMSettingOvsBridge *s_ovs_bridge = NULL;
+	NMSettingOvsPort *s_ovs_port = NULL;
+	NMSettingOvsInterface *s_ovs_iface = NULL;
+	NMSettingOvsPatch *s_ovs_patch = NULL;
+	const char *ovs_iface_type = NULL;
+
+	switch (TEST_CASE) {
+	case 1:
+		con = nmtst_create_minimal_connection ("test_connection_ovs_ifname_bridge",
+		                                       NULL,
+		                                       NM_SETTING_OVS_BRIDGE_SETTING_NAME, &s_con);
+		s_ovs_bridge = nm_connection_get_setting_ovs_bridge (con);
+		g_assert (s_ovs_bridge);
+		break;
+	case 2:
+		con = nmtst_create_minimal_connection ("test_connection_ovs_ifname_port",
+		                                       NULL,
+		                                       NM_SETTING_OVS_PORT_SETTING_NAME, &s_con);
+
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER,
+		              "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE,
+		              NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+		              NULL);
+
+		s_ovs_port = nm_connection_get_setting_ovs_port (con);
+		g_assert (s_ovs_port);
+		break;
+	case 3:
+		con = nmtst_create_minimal_connection ("test_connection_ovs_ifname_interface_patch",
+		                                       NULL,
+		                                       NM_SETTING_OVS_INTERFACE_SETTING_NAME, &s_con);
+		s_ovs_iface = nm_connection_get_setting_ovs_interface (con);
+		g_assert (s_ovs_iface);
+
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER,
+		              "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE,
+		              NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		g_object_set (s_ovs_iface,
+		              NM_SETTING_OVS_INTERFACE_TYPE,
+		              "patch",
+		              NULL);
+
+		s_ovs_patch = NM_SETTING_OVS_PATCH (nm_setting_ovs_patch_new());
+		g_assert (s_ovs_patch);
+
+		g_object_set (s_ovs_patch,
+		              NM_SETTING_OVS_PATCH_PEER, "1.2.3.4",
+		              NULL);
+
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_patch));
+		s_ovs_patch = nm_connection_get_setting_ovs_patch (con);
+		g_assert (s_ovs_patch);
+		ovs_iface_type = "patch";
+		break;
+	case 4:
+		con = nmtst_create_minimal_connection ("test_connection_ovs_ifname_interface_internal",
+		                                       NULL,
+		                                       NM_SETTING_OVS_INTERFACE_SETTING_NAME, &s_con);
+		s_ovs_iface = nm_connection_get_setting_ovs_interface (con);
+		g_assert (s_ovs_iface);
+
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER,
+		              "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE,
+		              NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		g_object_set (s_ovs_iface,
+		              NM_SETTING_OVS_INTERFACE_TYPE,
+		              "internal",
+		              NULL);
+		ovs_iface_type = "internal";
+		break;
+	case 5:
+		con = nmtst_create_minimal_connection ("test_connection_ovs_ifname_interface_system",
+		                                       NULL,
+		                                       NM_SETTING_WIRED_SETTING_NAME, &s_con);
+
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER,
+		              "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE,
+		              NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		s_ovs_iface = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new());
+		g_assert (s_ovs_iface);
+
+		g_object_set (s_ovs_iface,
+		              NM_SETTING_OVS_INTERFACE_TYPE,
+		              "system",
+		              NULL);
+
+		nm_connection_add_setting (con, NM_SETTING (s_ovs_iface));
+		s_ovs_iface = nm_connection_get_setting_ovs_interface (con);
+		g_assert (s_ovs_iface);
+
+		ovs_iface_type = "system";
+		break;
+	case 6:
+		con = nmtst_create_minimal_connection ("test_connection_ovs_ifname_interface_dpdk",
+		                                       NULL,
+		                                       NM_SETTING_OVS_INTERFACE_SETTING_NAME, &s_con);
+		s_ovs_iface = nm_connection_get_setting_ovs_interface (con);
+		g_assert (s_ovs_iface);
+
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER,
+		              "master0",
+		              NM_SETTING_CONNECTION_SLAVE_TYPE,
+		              NM_SETTING_OVS_PORT_SETTING_NAME,
+		              NULL);
+
+		g_object_set (s_ovs_iface,
+		              NM_SETTING_OVS_INTERFACE_TYPE,
+		              "dpdk",
+		              NULL);
+		ovs_iface_type = "dpdk";
+		break;
+	}
+
+	if (!nm_streq0 (ovs_iface_type, "system")) {
+		/* wrong: contains backward slash */
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME, "ovs\\0",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con,
+		                                        NM_CONNECTION_ERROR,
+		                                        NM_CONNECTION_ERROR_INVALID_PROPERTY);
+
+		/* wrong: contains forward slash */
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME, "ovs/0",
+		              NULL);
+		nmtst_assert_connection_unnormalizable (con,
+		                                        NM_CONNECTION_ERROR,
+		                                        NM_CONNECTION_ERROR_INVALID_PROPERTY);
+	}
+
+	/* wrong: contains space */
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "ovs 0",
+	              NULL);
+	nmtst_assert_connection_unnormalizable (con,
+	                                        NM_CONNECTION_ERROR,
+	                                        NM_CONNECTION_ERROR_INVALID_PROPERTY);
+
+	/* good */
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "ovs0",
+	              NULL);
+	nmtst_assert_connection_verifies (con);
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "ovs-br0",
+	              NULL);
+	nmtst_assert_connection_verifies (con);
+
+	/* good if bridge, port, or patch interface */
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "ovs123123123123130123123",
+	              NULL);
+
+	if (!ovs_iface_type || nm_streq (ovs_iface_type, "patch"))
+		nmtst_assert_connection_verifies (con);
+	else {
+		nmtst_assert_connection_unnormalizable (con,
+		                                        NM_CONNECTION_ERROR,
+		                                        NM_CONNECTION_ERROR_INVALID_PROPERTY);
+	}
+}
+
+
+
 /*****************************************************************************/
 
 NMTST_DEFINE ();
@@ -8411,6 +8598,13 @@ int main (int argc, char **argv)
 	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/11", GUINT_TO_POINTER (11), test_connection_normalize_ovs_interface_type_ovs_interface);
 	g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/12", GUINT_TO_POINTER (12), test_connection_normalize_ovs_interface_type_ovs_interface);
 
+	g_test_add_data_func ("/core/general/test_connection_ovs_ifname/1", GUINT_TO_POINTER (1), test_connection_ovs_ifname);
+	g_test_add_data_func ("/core/general/test_connection_ovs_ifname/2", GUINT_TO_POINTER (2), test_connection_ovs_ifname);
+	g_test_add_data_func ("/core/general/test_connection_ovs_ifname/3", GUINT_TO_POINTER (3), test_connection_ovs_ifname);
+	g_test_add_data_func ("/core/general/test_connection_ovs_ifname/4", GUINT_TO_POINTER (4), test_connection_ovs_ifname);
+	g_test_add_data_func ("/core/general/test_connection_ovs_ifname/5", GUINT_TO_POINTER (5), test_connection_ovs_ifname);
+	g_test_add_data_func ("/core/general/test_connection_ovs_ifname/6", GUINT_TO_POINTER (6), test_connection_ovs_ifname);
+
 	g_test_add_func ("/core/general/test_setting_connection_permissions_helpers", test_setting_connection_permissions_helpers);
 	g_test_add_func ("/core/general/test_setting_connection_permissions_property", test_setting_connection_permissions_property);