summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiří Klimeš <jklimes@redhat.com>2015-06-19 12:15:12 +0200
committerJiří Klimeš <jklimes@redhat.com>2015-06-19 12:15:12 +0200
commit2f51ba50df8341370ab1f480f9ec6d60cd32ece2 (patch)
tree29a4eafb9d781f5ed96821e860872971621abef8
parent517e9fa0f062e286f893f41770d0562c4baf0c51 (diff)
parent1468f4edd2b57e5a76c11df2e397d050b0613d82 (diff)
merge branch 'th/connection-defaults-bgo695383'
- support new [connection] section in NetworkManager.conf - allow configuring a default value for ipvx.route-metric - allow configuring a default value for ipv6.ip6-privacy and read the fallback configuration from sysctl. https://bugzilla.gnome.org/show_bug.cgi?id=695383 https://bugzilla.gnome.org/show_bug.cgi?id=721200 https://bugzilla.redhat.com/show_bug.cgi?id=1187525 (cherry-picked from commit 18ecf48d7a9d03194db1b65ef46e386284426f89)
-rw-r--r--libnm-core/nm-setting-ip6-config.c10
-rw-r--r--libnm/nm-device.c3
-rw-r--r--man/NetworkManager.conf.xml.in87
-rw-r--r--src/NetworkManagerUtils.c38
-rw-r--r--src/NetworkManagerUtils.h1
-rw-r--r--src/devices/nm-device-generic.c9
-rw-r--r--src/devices/nm-device.c193
-rw-r--r--src/devices/nm-device.h3
-rw-r--r--src/devices/wwan/nm-device-modem.c13
-rw-r--r--src/nm-config-data.c122
-rw-r--r--src/nm-config-data.h4
-rw-r--r--src/nm-config.c16
-rw-r--r--src/nm-config.h4
-rw-r--r--src/tests/config/NetworkManager.conf24
-rw-r--r--src/tests/config/test-config.c51
15 files changed, 492 insertions, 86 deletions
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index 29ca551525..7e3ae4d4da 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -504,9 +504,15 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
* enabled, it makes the kernel generate a temporary IPv6 address in
* addition to the public one generated from MAC address via modified
* EUI-64. This enhances privacy, but could cause problems in some
- * applications, on the other hand. The permitted values are: 0: disabled,
- * 1: enabled (prefer public address), 2: enabled (prefer temporary
+ * applications, on the other hand. The permitted values are: -1: unknown,
+ * 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary
* addresses).
+ *
+ * Having a per-connection setting set to "-1" (unknown) means fallback to
+ * global configuration "ipv6.ip6-privacy".
+ *
+ * If also global configuration is unspecified or set to "-1", fallback to read
+ * "/proc/sys/net/ipv6/conf/default/use_tempaddr".
**/
/* ---ifcfg-rh---
* property: ip6-privacy
diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index e80ac707d8..cd14c0e902 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -979,6 +979,9 @@ nm_device_get_type_description (NMDevice *device)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
const char *desc, *typename;
+ /* BEWARE: this function should return the same value
+ * as nm_device_get_type_description() in nm-core. */
+
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
if (priv->type_description)
diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in
index 79db1a2de2..fac4616ff6 100644
--- a/man/NetworkManager.conf.xml.in
+++ b/man/NetworkManager.conf.xml.in
@@ -440,6 +440,88 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
</refsect1>
<refsect1>
+ <title><literal>connection</literal> section</title>
+ <para>This section allows to specify default values for
+ connections. Not all properties can be overwritten, only a selected
+ list below. You can have multiple <literal>connection</literal>
+ sections, by having different sections with a name that all start
+ with "connection".</para>
+ <para>
+ Example:
+<programlisting>
+[connection]
+ipv6.ip6-privacy=0
+
+[connection-wifi-wlan0]
+match-device=interface-name:wlan0
+ipv4.route-metric=50
+
+[connection-wifi-other]
+match-device=type:wifi
+ipv4.route-metric=55
+ipv6.ip6-privacy=1
+</programlisting>
+ </para>
+
+ <para>
+ The sections are considered in order of appearance, with the
+ exception that the <literal>[connection]</literal> section is always
+ considered last. In the example above, this order is <literal>[connection-wifi-wlan0]</literal>,
+ <literal>[connection-wlan-other]</literal>, and <literal>[connection]</literal>.
+ When checking for a default configuration value, the section are searched until
+ the requested value is found.
+ In the example above, "ipv4.route-metric" for wlan0 interface is set to 50,
+ and for all other Wi-Fi typed interfaces to 55. Also, Wi-Fi devices would have
+ IPv6 private addresses enabled by default, but other devices would have it disabled.
+ Note that also "wlan0" gets "ipv6.ip6-privacy=1", because although the section
+ "[connection-wifi-wlan0]" matches the device, it does not contain that property
+ and the search continues.
+ </para>
+
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><varname>match-device</varname></term>
+ <listitem><para>An optional device spec that restricts
+ when the section applies. See <xref linkend="device-spec"/>
+ for the possible values.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>stop-match</varname></term>
+ <listitem><para>An optional boolean value which defaults to
+ <literal>no</literal>. If the section matches (based on
+ <literal>match-device</literal>), further sections will not be
+ considered even if the property in question is not present. In
+ the example above, if <literal>[connection-wifi-wlan0]</literal> would
+ have <literal>stop-match</literal> set to <literal>yes</literal>,
+ its <literal>ipv6.ip6-privacy</literal> value would be
+ unspecified.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ The following properties are supported to have their default values configured:
+ <variablelist>
+ <varlistentry>
+ <term><varname>ipv4.route-metric</varname></term>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ipv6.ip6-privacy</varname></term>
+ <listitem><para>If <literal>ipv6.ip6-privacy</literal> is unset, use the content of
+ "/proc/sys/net/ipv6/conf/default/use_tempaddr" as last fallback.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ipv6.route-metric</varname></term>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+
+ <refsect1>
<title><literal>connectivity</literal> section</title>
<para>This section controls NetworkManager's optional connectivity
checking functionality. This allows NetworkManager to detect
@@ -607,6 +689,11 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
<listitem><para>Match the device based on the subchannel address. Globbing is not supported</para></listitem>
</varlistentry>
<varlistentry>
+ <term>type:TYPE</term>
+ <listitem><para>Match the device type. Valid type names are as reported by "<literal>nmcli -f GENERAL.TYPE device show</literal>".
+ Globbing is not supported.</para></listitem>
+ </varlistentry>
+ <varlistentry>
<term>except:SPEC</term>
<listitem><para>Negative match of a device. <literal>SPEC</literal> must be explicitly qualified with
a prefix such as <literal>interface-name:</literal>. A negative match has higher priority then the positive
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 980b6a8d96..a989813d47 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -869,6 +869,7 @@ nm_utils_find_helper(const char *progname, const char *try_first, GError **error
#define MAC_TAG "mac:"
#define INTERFACE_NAME_TAG "interface-name:"
+#define DEVICE_TYPE_TAG "type:"
#define SUBCHAN_TAG "s390-subchannels:"
#define EXCEPT_TAG "except:"
@@ -884,6 +885,37 @@ _match_except (const char *spec_str, gboolean *out_except)
}
NMMatchSpecMatchType
+nm_match_spec_device_type (const GSList *specs, const char *device_type)
+{
+ const GSList *iter;
+ NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
+
+ if (!device_type || !*device_type)
+ return NM_MATCH_SPEC_NO_MATCH;
+
+ for (iter = specs; iter; iter = g_slist_next (iter)) {
+ const char *spec_str = iter->data;
+ gboolean except;
+
+ if (!spec_str || !*spec_str)
+ continue;
+
+ spec_str = _match_except (spec_str, &except);
+
+ if (g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)) != 0)
+ continue;
+
+ spec_str += STRLEN (DEVICE_TYPE_TAG);
+ if (strcmp (spec_str, device_type) == 0) {
+ if (except)
+ return NM_MATCH_SPEC_NEG_MATCH;
+ match = NM_MATCH_SPEC_MATCH;
+ }
+ }
+ return match;
+}
+
+NMMatchSpecMatchType
nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
{
const GSList *iter;
@@ -901,7 +933,8 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
spec_str = _match_except (spec_str, &except);
if ( !g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))
- || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG)))
+ || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))
+ || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)))
continue;
if (!g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG)))
@@ -937,7 +970,8 @@ nm_match_spec_interface_name (const GSList *specs, const char *interface_name)
spec_str = _match_except (spec_str, &except);
if ( !g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG))
- || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG)))
+ || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))
+ || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)))
continue;
if (!g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))) {
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 3bd7252876..03836abd5d 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -102,6 +102,7 @@ typedef enum {
NM_MATCH_SPEC_NEG_MATCH = 2,
} NMMatchSpecMatchType;
+NMMatchSpecMatchType nm_match_spec_device_type (const GSList *specs, const char *device_type);
NMMatchSpecMatchType nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
NMMatchSpecMatchType nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
NMMatchSpecMatchType nm_match_spec_interface_name (const GSList *specs, const char *interface_name);
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 8b328e07fe..5841d834ff 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -54,6 +54,14 @@ get_generic_capabilities (NMDevice *dev)
return NM_DEVICE_CAP_NONE;
}
+static const char *
+get_type_description (NMDevice *device)
+{
+ if (NM_DEVICE_GENERIC_GET_PRIVATE (device)->type_description)
+ return NM_DEVICE_GENERIC_GET_PRIVATE (device)->type_description;
+ return NM_DEVICE_CLASS (nm_device_generic_parent_class)->get_type_description (device);
+}
+
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection)
{
@@ -184,6 +192,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
object_class->set_property = set_property;
parent_class->get_generic_capabilities = get_generic_capabilities;
+ parent_class->get_type_description = get_type_description;
parent_class->check_connection_compatible = check_connection_compatible;
parent_class->update_connection = update_connection;
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index aab14708bc..3d5cda21b9 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -189,6 +189,7 @@ typedef struct {
int ip_ifindex;
NMDeviceType type;
char * type_desc;
+ char * type_description;
NMDeviceCapabilities capabilities;
char * driver;
char * driver_version;
@@ -734,50 +735,63 @@ nm_device_get_priority (NMDevice *self)
return 11000;
}
-guint32
-nm_device_get_ip4_route_metric (NMDevice *self)
+static guint32
+_get_ipx_route_metric (NMDevice *self,
+ gboolean is_v4)
{
+ char *value;
+ gint64 route_metric;
+ NMSettingIPConfig *s_ip;
NMConnection *connection;
- NMSettingIPConfig *s_ip = NULL;
- gint64 route_metric = -1;
g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
connection = nm_device_get_connection (self);
- if (connection)
- s_ip = nm_connection_get_setting_ip4_config (connection);
+ if (connection) {
+ s_ip = is_v4
+ ? nm_connection_get_setting_ip4_config (connection)
+ : nm_connection_get_setting_ip6_config (connection);
- /* Slave interfaces don't have IP settings, but we may get here when
- * external changes are made or when noticing IP changes when starting
- * the slave connection.
- */
- if (s_ip)
- route_metric = nm_setting_ip_config_get_route_metric (s_ip);
+ /* Slave interfaces don't have IP settings, but we may get here when
+ * external changes are made or when noticing IP changes when starting
+ * the slave connection.
+ */
+ if (s_ip) {
+ route_metric = nm_setting_ip_config_get_route_metric (s_ip);
+ if (route_metric >= 0)
+ goto out;
+ }
+ }
+
+ /* use the current NMConfigData, which makes this configuration reloadable.
+ * Note that that means that the route-metric might change between SIGHUP.
+ * You must cache the returned value if that is a problem. */
+ value = nm_config_data_get_connection_default (nm_config_get_data (nm_config_get ()),
+ is_v4 ? "ipv4.route-metric" : "ipv6.route-metric", self);
+ if (value) {
+ route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
+ g_free (value);
- return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
+ if (route_metric >= 0)
+ goto out;
+ }
+ route_metric = nm_device_get_priority (self);
+out:
+ if (!is_v4)
+ route_metric = nm_utils_ip6_route_metric_normalize (route_metric);
+ return route_metric;
}
guint32
-nm_device_get_ip6_route_metric (NMDevice *self)
+nm_device_get_ip4_route_metric (NMDevice *self)
{
- NMConnection *connection;
- NMSettingIPConfig *s_ip = NULL;
- gint64 route_metric = -1;
-
- g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
-
- connection = nm_device_get_connection (self);
- if (connection)
- s_ip = nm_connection_get_setting_ip6_config (connection);
-
- /* Slave interfaces don't have IP settings, but we may get here when
- * external changes are made or when noticing IP changes when starting
- * the slave connection.
- */
- if (s_ip)
- route_metric = nm_setting_ip_config_get_route_metric (s_ip);
+ return _get_ipx_route_metric (self, TRUE);
+}
- return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
+guint32
+nm_device_get_ip6_route_metric (NMDevice *self)
+{
+ return _get_ipx_route_metric (self, FALSE);
}
const NMPlatformIP4Route *
@@ -818,6 +832,34 @@ nm_device_get_type_desc (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->type_desc;
}
+const char *
+nm_device_get_type_description (NMDevice *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ /* Beware: this function should return the same
+ * value as nm_device_get_type_description() in libnm. */
+
+ return NM_DEVICE_GET_CLASS (self)->get_type_description (self);
+}
+
+static const char *
+get_type_description (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (!priv->type_description) {
+ const char *typename;
+
+ typename = G_OBJECT_TYPE_NAME (self);
+ if (g_str_has_prefix (typename, "NMDevice"))
+ typename += 8;
+ priv->type_description = g_ascii_strdown (typename, -1);
+ }
+
+ return priv->type_description;
+}
+
gboolean
nm_device_has_carrier (NMDevice *self)
{
@@ -4555,8 +4597,10 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
}
}
+/************************************************************************/
+
static NMSettingIP6ConfigPrivacy
-use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
+_ip6_privacy_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
{
switch (use_tempaddr) {
case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
@@ -4568,45 +4612,51 @@ use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr)
}
}
-/* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or
- * /lib/sysctl.d/sysctl.conf
- */
static NMSettingIP6ConfigPrivacy
-ip6_use_tempaddr (void)
+_ip6_privacy_get (NMDevice *self)
{
- char *contents = NULL;
- const char *group_name = "[forged_group]\n";
- char *sysctl_data = NULL;
- GKeyFile *keyfile;
- GError *error = NULL;
- gint tmp;
- NMSettingIP6ConfigPrivacy ret = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+ NMSettingIP6ConfigPrivacy ip6_privacy;
+ gs_free char *value = NULL;
+ NMConnection *connection;
- /* Read file contents to a string. */
- if (!g_file_get_contents ("/etc/sysctl.conf", &contents, NULL, NULL))
- if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, NULL, NULL))
- return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+ g_return_val_if_fail (self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
- /* Prepend a group so that we can use GKeyFile parser. */
- sysctl_data = g_strdup_printf ("%s%s", group_name, contents);
+ /* 1.) First look at the per-connection setting. If it is not -1 (unknown),
+ * use it. */
+ connection = nm_device_get_connection (self);
+ if (connection) {
+ NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
- keyfile = g_key_file_new ();
- if (!g_key_file_load_from_data (keyfile, sysctl_data, -1, G_KEY_FILE_NONE, NULL))
- goto done;
+ if (s_ip6) {
+ ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6));
+ ip6_privacy = _ip6_privacy_clamp (ip6_privacy);
+ if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
+ return ip6_privacy;
+ }
+ }
- tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error);
- if (error == NULL)
- ret = use_tempaddr_clamp (tmp);
+ value = nm_config_data_get_connection_default (nm_config_get_data (nm_config_get ()),
+ "ipv6.ip6-privacy", self);
-done:
- g_free (contents);
- g_free (sysctl_data);
- g_clear_error (&error);
- g_key_file_free (keyfile);
+ /* 2.) use the default value from the configuration. */
+ ip6_privacy = _nm_utils_ascii_str_to_int64 (value, 10,
+ NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
+ NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
+ NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+ if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
+ return ip6_privacy;
- return ret;
+ /* 3.) No valid default-value configured. Fallback to reading sysctl.
+ *
+ * Instead of reading static config files in /etc, just read the current sysctl value.
+ * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves
+ * the "default" entry untouched. */
+ ip6_privacy = nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, "/proc/sys/net/ipv6/conf/default/use_tempaddr", NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+ return _ip6_privacy_clamp (ip6_privacy);
}
+/****************************************************************/
+
static gboolean
ip6_requires_slaves (NMConnection *connection)
{
@@ -4705,18 +4755,7 @@ act_stage3_ip6_config_start (NMDevice *self,
/* Re-enable IPv6 on the interface */
set_disable_ipv6 (self, "0");
- /* Enable/disable IPv6 Privacy Extensions.
- * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
- * use that value instead of per-connection value.
- */
- ip6_privacy = ip6_use_tempaddr ();
- if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) {
- NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
-
- if (s_ip6)
- ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6));
- }
- ip6_privacy = use_tempaddr_clamp (ip6_privacy);
+ ip6_privacy = _ip6_privacy_get (self);
if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
if (!addrconf6_start (self, ip6_privacy)) {
@@ -8354,6 +8393,10 @@ spec_match_list (NMDevice *self, const GSList *specs)
m = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
matched = MAX (matched, m);
}
+ if (matched != NM_MATCH_SPEC_NEG_MATCH) {
+ m = nm_match_spec_device_type (specs, nm_device_get_type_description (self));
+ matched = MAX (matched, m);
+ }
return matched;
}
@@ -8638,6 +8681,7 @@ finalize (GObject *object)
g_free (priv->driver_version);
g_free (priv->firmware_version);
g_free (priv->type_desc);
+ g_free (priv->type_description);
g_free (priv->dhcp_anycast_address);
g_hash_table_unref (priv->ip6_saved_properties);
@@ -8655,7 +8699,7 @@ set_property (GObject *object, guint prop_id,
NMPlatformLink *platform_device;
const char *hw_addr, *p;
guint count;
-
+
switch (prop_id) {
case PROP_PLATFORM_DEVICE:
platform_device = g_value_get_pointer (value);
@@ -8917,6 +8961,7 @@ nm_device_class_init (NMDeviceClass *klass)
klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
klass->have_any_ready_slaves = have_any_ready_slaves;
+ klass->get_type_description = get_type_description;
klass->spec_match_list = spec_match_list;
klass->can_auto_connect = can_auto_connect;
klass->check_connection_compatible = check_connection_compatible;
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index b44d2c2721..ef61dba72d 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -210,6 +210,8 @@ typedef struct {
/* Sync deactivating (in the DISCONNECTED phase) */
void (* deactivate) (NMDevice *self);
+ const char *(*get_type_description) (NMDevice *self);
+
NMMatchSpecMatchType (* spec_match_list) (NMDevice *self, const GSList *specs);
/* Update the connection with currently configured L2 settings */
@@ -276,6 +278,7 @@ int nm_device_get_ip_ifindex(NMDevice *dev);
const char * nm_device_get_driver (NMDevice *dev);
const char * nm_device_get_driver_version (NMDevice *dev);
const char * nm_device_get_type_desc (NMDevice *dev);
+const char * nm_device_get_type_description (NMDevice *dev);
NMDeviceType nm_device_get_device_type (NMDevice *dev);
int nm_device_get_priority (NMDevice *dev);
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index c02f5def9b..ca724c046c 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -372,6 +372,18 @@ get_generic_capabilities (NMDevice *device)
return NM_DEVICE_CAP_IS_NON_KERNEL;
}
+static const char *
+get_type_description (NMDevice *device)
+{
+ NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+
+ if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS))
+ return "gsm";
+ if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO))
+ return "cdma";
+ return NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_type_description (device);
+}
+
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection)
{
@@ -741,6 +753,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
object_class->constructed = constructed;
device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->get_type_description = get_type_description;
device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available;
device_class->complete_connection = complete_connection;
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 2569f7ca30..e53d8fbb11 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -27,6 +27,19 @@
#include "nm-device.h"
#include "gsystem-local-alloc.h"
#include "nm-core-internal.h"
+#include "nm-utils-internal.h"
+
+typedef struct {
+ char *group_name;
+ gboolean stop_match;
+ struct {
+ /* have a separate boolean field @has, because a @spec with
+ * value %NULL does not necessarily mean, that the property
+ * "match-device" was unspecified. */
+ gboolean has;
+ GSList *spec;
+ } match_device;
+} ConnectionInfo;
typedef struct {
char *config_main_file;
@@ -34,6 +47,10 @@ typedef struct {
GKeyFile *keyfile;
+ /* A zero-terminated list of pre-processed information from the
+ * [connection] sections. This is to speed up lookup. */
+ ConnectionInfo *connection_infos;
+
struct {
char *uri;
char *response;
@@ -163,6 +180,100 @@ nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *devic
/************************************************************************/
+char *
+nm_config_data_get_connection_default (const NMConfigData *self,
+ const char *property,
+ NMDevice *device)
+{
+ NMConfigDataPrivate *priv;
+ const ConnectionInfo *connection_info;
+
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (property && *property, NULL);
+ g_return_val_if_fail (strchr (property, '.'), NULL);
+
+ priv = NM_CONFIG_DATA_GET_PRIVATE (self);
+
+ if (!priv->connection_infos)
+ return NULL;
+
+ for (connection_info = &priv->connection_infos[0]; connection_info->group_name; connection_info++) {
+ char *value;
+ gboolean match;
+
+ value = g_key_file_get_value (priv->keyfile, connection_info->group_name, property, NULL);
+ if (!value && !connection_info->stop_match)
+ continue;
+
+ match = TRUE;
+ if (connection_info->match_device.has)
+ match = device && nm_device_spec_match_list (device, connection_info->match_device.spec);
+
+ if (match)
+ return value;
+ g_free (value);
+ }
+ return NULL;
+}
+
+static ConnectionInfo *
+_get_connection_infos (GKeyFile *keyfile)
+{
+ char **groups;
+ guint i;
+ char *connection_tag = NULL;
+ GSList *connection_groups = NULL;
+ ConnectionInfo *connection_infos = NULL;
+
+ /* get the list of existing [connection.\+] sections that we consider
+ * for nm_config_data_get_connection_default(). Also, get them
+ * in the right order. */
+ groups = g_key_file_get_groups (keyfile, NULL);
+ for (i = 0; groups && groups[i]; i++) {
+ if (g_str_has_prefix (groups[i], "connection")) {
+ if (strlen (groups[i]) == STRLEN ("connection"))
+ connection_tag = groups[i];
+ else
+ connection_groups = g_slist_prepend (connection_groups, groups[i]);
+ } else
+ g_free (groups[i]);
+ }
+ g_free (groups);
+ if (connection_tag) {
+ /* We want the group "connection" checked at last, so that
+ * all other "connection.\+" have preference. Those other
+ * groups are checked in order of appearance. */
+ connection_groups = g_slist_prepend (connection_groups, connection_tag);
+ }
+ if (connection_groups) {
+ guint len = g_slist_length (connection_groups);
+ GSList *iter;
+
+ connection_infos = g_new0 (ConnectionInfo, len + 1);
+ for (iter = connection_groups; iter; iter = iter->next) {
+ ConnectionInfo *connection_info;
+ char *value;
+
+ nm_assert (len >= 1);
+ connection_info = &connection_infos[--len];
+ connection_info->group_name = iter->data;
+
+ value = g_key_file_get_value (keyfile, iter->data, "match-device", NULL);
+ if (value) {
+ connection_info->match_device.has = TRUE;
+ connection_info->match_device.spec = nm_match_spec_split (value);
+ g_free (value);
+ }
+ connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, iter->data, "stop-match", FALSE);
+ }
+ g_slist_free (connection_groups);
+ }
+
+ return connection_infos;
+}
+
+/************************************************************************/
+
static gboolean
_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b)
{
@@ -312,6 +423,7 @@ static void
finalize (GObject *gobject)
{
NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (gobject);
+ guint i;
g_free (priv->config_main_file);
g_free (priv->config_description);
@@ -327,6 +439,14 @@ finalize (GObject *gobject)
g_slist_free_full (priv->ignore_carrier, g_free);
g_slist_free_full (priv->assume_ipv6ll_only, g_free);
+ if (priv->connection_infos) {
+ for (i = 0; priv->connection_infos[i].group_name; i++) {
+ g_free (priv->connection_infos[i].group_name);
+ g_slist_free_full (priv->connection_infos[i].match_device.spec, g_free);
+ }
+ g_free (priv->connection_infos);
+ }
+
g_key_file_unref (priv->keyfile);
G_OBJECT_CLASS (nm_config_data_parent_class)->finalize (gobject);
@@ -344,6 +464,8 @@ constructed (GObject *object)
NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self);
char *interval;
+ priv->connection_infos = _get_connection_infos (priv->keyfile);
+
priv->connectivity.uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL);
priv->connectivity.response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL);
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index 0c3e3d1455..07140f82b6 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -92,6 +92,10 @@ const char *nm_config_data_get_dns_mode (const NMConfigData *self);
gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device);
gboolean nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device);
+char *nm_config_data_get_connection_default (const NMConfigData *self,
+ const char *property,
+ NMDevice *device);
+
G_END_DECLS
#endif /* NM_CONFIG_DATA_H */
diff --git a/src/nm-config.c b/src/nm-config.c
index 25e4902bde..c7e61b559e 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -107,11 +107,11 @@ static void _set_config_data (NMConfig *self, NMConfigData *new_data);
/************************************************************************/
-static gboolean
-_get_bool_value (GKeyFile *keyfile,
- const char *section,
- const char *key,
- gboolean default_value)
+gboolean
+nm_config_keyfile_get_boolean (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ gboolean default_value)
{
gboolean value = default_value;
char *str;
@@ -828,9 +828,9 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
if (!priv->plugins)
priv->plugins = g_new0 (char *, 1);
- priv->monitor_connection_files = _get_bool_value (keyfile, "main", "monitor-connection-files", FALSE);
+ priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, "main", "monitor-connection-files", FALSE);
- priv->auth_polkit = _get_bool_value (keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
+ priv->auth_polkit = nm_config_keyfile_get_boolean (keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
priv->dhcp_client = g_key_file_get_value (keyfile, "main", "dhcp", NULL);
@@ -839,7 +839,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
priv->debug = g_key_file_get_value (keyfile, "main", "debug", NULL);
- priv->configure_and_quit = _get_bool_value (keyfile, "main", "configure-and-quit", FALSE);
+ priv->configure_and_quit = nm_config_keyfile_get_boolean (keyfile, "main", "configure-and-quit", FALSE);
no_auto_default_orig_list = nm_config_get_device_match_spec (keyfile, "main", "no-auto-default");
diff --git a/src/nm-config.h b/src/nm-config.h
index e72132af9d..29d6566323 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -89,6 +89,10 @@ NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error);
void nm_config_reload (NMConfig *config);
GKeyFile *nm_config_create_keyfile (void);
+gboolean nm_config_keyfile_get_boolean (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ gboolean default_value);
GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key);
G_END_DECLS
diff --git a/src/tests/config/NetworkManager.conf b/src/tests/config/NetworkManager.conf
index 401ba48a72..36113661f2 100644
--- a/src/tests/config/NetworkManager.conf
+++ b/src/tests/config/NetworkManager.conf
@@ -13,3 +13,27 @@ response=Hello
[extra-section]
extra-key=some value
+
+
+
+[connection]
+ipv4.route-metric=50
+ipv6.ip6_privacy=0
+dummy.test1=no
+dummy.test2=no
+
+[connection.dev51]
+match-device=mac:00:00:00:00:00:51
+stop-match=yes
+ipv4.route-metric=51
+dummy.test1=yes
+
+[connection.dev52]
+match-device=mac:00:00:00:00:00:52
+ipv4.route-metric=52
+
+[connection.public]
+match-device=interface-name:wlan1
+# match-wifi is not yet implemented. Just an idea what could be useful.
+match-wifi=ssid:*[Ss]tarbucks*|*University*
+ipv6.ip6_privacy=2
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index 67ba5904ec..7ce3c1ee7c 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -94,6 +94,9 @@ test_config_simple (void)
GError *error = NULL;
const char **plugins;
char *value;
+ gs_unref_object NMDevice *dev50 = nm_test_device_new ("00:00:00:00:00:50");
+ gs_unref_object NMDevice *dev51 = nm_test_device_new ("00:00:00:00:00:51");
+ gs_unref_object NMDevice *dev52 = nm_test_device_new ("00:00:00:00:00:52");
config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir", NULL);
@@ -122,6 +125,54 @@ test_config_simple (void)
g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
g_clear_error (&error);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection", "ipv6.ip6_privacy", NULL);
+ g_assert_cmpstr (value, ==, "0");
+ g_free (value);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection.dev51", "ipv4.route-metric", NULL);
+ g_assert_cmpstr (value, ==, "51");
+ g_free (value);
+
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv6.route-metric", NULL);
+ g_assert_cmpstr (value, ==, NULL);
+ g_free (value);
+
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", NULL);
+ g_assert_cmpstr (value, ==, "50");
+ g_free (value);
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", dev50);
+ g_assert_cmpstr (value, ==, "50");
+ g_free (value);
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", dev51);
+ g_assert_cmpstr (value, ==, "51");
+ g_free (value);
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", dev52);
+ g_assert_cmpstr (value, ==, "52");
+ g_free (value);
+
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev51);
+ g_assert_cmpstr (value, ==, "yes");
+ g_free (value);
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev50);
+ g_assert_cmpstr (value, ==, "no");
+ g_free (value);
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test2", dev51);
+ g_assert_cmpstr (value, ==, NULL);
+ g_free (value);
+
+ value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test2", dev50);
+ g_assert_cmpstr (value, ==, "no");
+ g_free (value);
+
+
g_object_unref (config);
}