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 18ecf48d7a)
This commit is contained in:
Jiří Klimeš 2015-06-19 12:15:12 +02:00
commit 2f51ba50df
15 changed files with 492 additions and 86 deletions

View file

@ -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

View file

@ -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)

View file

@ -439,6 +439,88 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
</para>
</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
@ -606,6 +688,11 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
<term>s390-subchannels:HWADDR</term>
<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

View file

@ -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:"
@ -883,6 +884,37 @@ _match_except (const char *spec_str, gboolean *out_except)
return spec_str;
}
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)
{
@ -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))) {

View file

@ -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);

View file

@ -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;

View file

@ -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;
}
}
return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
/* 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);
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_ip4_route_metric (NMDevice *self)
{
return _get_ipx_route_metric (self, TRUE);
}
guint32
nm_device_get_ip6_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 route_metric >= 0 ? route_metric : nm_device_get_priority (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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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 */

View file

@ -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");

View file

@ -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

View file

@ -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

View file

@ -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);
}