summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-05-15 11:36:28 +0200
committerThomas Haller <thaller@redhat.com>2015-06-04 14:31:51 +0200
commit444eccf62b1fbc5d287c88711aefe77a9c395f96 (patch)
treedc37767d0692e5bb2d9a7ddda118204a0714384f
parent11769db688ef5265adbe0a45a96623276e4c3256 (diff)
downloadNetworkManager-444eccf62b1fbc5d287c88711aefe77a9c395f96.tar.gz
config: support a [connection] section to NetworkManager.conf to specify connection defaults
Add support for a new section [connection] in NetworkManager.conf. If the connection leaves an option at "unknown"/"default", we can support overwriting the value from global configuration. We also support other sections that are named with "connection" as a prefix, such as [connection2], [connection-wifi]. This is to support multiple default values that can be applied depending on the used device. I think this has great potential. Only downside is that when the user looks at a connection value, it will see that it is unspecified. But the actually used value depends on the device type and might not be obvious. https://bugzilla.gnome.org/show_bug.cgi?id=695383 https://bugzilla.redhat.com/show_bug.cgi?id=1164677
-rw-r--r--man/NetworkManager.conf.xml.in71
-rw-r--r--src/nm-config-data.c122
-rw-r--r--src/nm-config-data.h4
-rw-r--r--src/tests/config/NetworkManager.conf24
-rw-r--r--src/tests/config/test-config.c51
5 files changed, 272 insertions, 0 deletions
diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in
index bfd857eb76..448d27ca72 100644
--- a/man/NetworkManager.conf.xml.in
+++ b/man/NetworkManager.conf.xml.in
@@ -454,6 +454,77 @@ 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.
+ This is just an example, currently these properties are not overwritable.
+ </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>
+ </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
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 41995a043f..73c36312f3 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -27,6 +27,19 @@
#include "gsystem-local-alloc.h"
#include "nm-device.h"
#include "nm-core-internal.h"
+#include "nm-macros-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;
@@ -172,6 +189,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)
{
@@ -324,6 +435,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);
@@ -340,6 +452,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);
@@ -357,6 +477,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 a8182f2839..78b04ec8c9 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -94,6 +94,10 @@ const char *nm_config_data_get_rc_manager (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/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 f4d72d333f..4a23f23fe5 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);
}